Каталог на сайте
ReplAI Feed — формат фида каталога
JSON-контракт для передачи товаров, акций и информационных страниц агенту ReplAI. Подходит для любого стека.
Универсальный способ передать AI-агенту ReplAI товары, акции и информационные страницы вашего сайта. Один контракт для любого стека: PHP, Python, Node, 1С-выгрузка или скрипт по расписанию.
Как это работает
Ничего отправлять не нужно: вы отдаёте JSON по постоянному URL, а ReplAI сам забирает его каждые 30 минут. Cron на вашем хостинге не требуется.
- Настройте на сайте страницу, отдающую фид, — например
https://мой-магазин.kg/replai-feed.json. - В кабинете ReplAI: Товары → Источники → Товары с вашего сайта → Подключить, вкладка «Другой сайт». Вставьте адрес в поле «Ссылка на выгрузку товаров» и нажмите «Подключить» — товары загрузятся сразу, и вы увидите, сколько их пришло. Ошибка последнего забора тоже видна в кабинете.
- Дальше каталог обновляется сам; забрать изменения раньше срока — кнопка «Обновить сейчас» в том же окне.
Ответ трактуется как полный снимок (snapshot: true), если в JSON явно не
указано иное.
Требования к URL: http(s), без логина/пароля в адресе, доступен из интернета
(не localhost и не внутренний IP), без редиректов — давайте конечный адрес.
Один ответ — до 25 МБ; каталог, который в этот размер не влезает, отдавайте
страницами. Если фид «секретный», добавьте свой
токен в query — мы сохраним адрес как есть.
Режим обновления
Рекомендуемый режим — полный снимок ("snapshot": true): отдавайте по URL
весь актуальный каталог, всё пропавшее из снимка убирается автоматически. Это
же значение подставляется по умолчанию, если поля в ответе нет.
Что значит «убирается»:
- вариация, на которую не ссылается ни один заказ, удаляется: пропадает из кабинета и поиска агента, а её артикул освобождается для других записей;
- вариация, которая уже есть в заказах, скрывается: агент её не предлагает, но заказ сохраняет ссылку на неё;
- товар, у которого не осталось ни одной вариации, удаляется вместе с ними.
Вернули запись в следующий снимок с тем же external_id — вариация и её товар
возвращаются с прежними идентификаторами. Удалённый товар при этом снова
занимает место в лимите тарифа: если каталог агента уже на пределе, товар не
создаётся, а запись пропускается с пометкой plan_limit_reached.
Частичная выдача ("snapshot": false) ничего не убирает — она дополняет и
обновляет то, что уже загружено. Нужна редко: например, пока каталог собирается
по частям.
Большой каталог — страницы
Каталог на десятки тысяч позиций в один ответ не влезает, да и собирать его целиком на хостинге тяжело. Такой фид отдаётся страницами: мы идём по ним сами, пока они не кончатся, и только потом применяем снимок целиком.
Как это выглядит: к вашему адресу мы дописываем параметры и запрашиваем страницы одну за другой.
…/replai-feed.json?page=1&per_page=1000
…/replai-feed.json?page=2&per_page=1000&cursor=1000 ← cursor из прошлого ответа
…/replai-feed.json?page=3&per_page=1000&cursor=2000
| Параметр запроса | Что означает |
|---|---|
page |
Номер страницы, начиная с 1 |
per_page |
Сколько товаров мы просим (сейчас 1000) |
cursor |
Значение next_cursor из предыдущего ответа; на первой странице отсутствует |
Ответ добавляет к обычному телу три поля:
{
"snapshot": true,
"page": 2,
"has_more": true,
"next_cursor": "2000",
"products": [ "…1000 товаров…" ],
"pages": []
}
| Поле ответа | Тип | Описание |
|---|---|---|
has_more |
bool | true — есть ещё страницы, мы придём за следующей |
next_cursor |
string ≤500 | Что вернуть в cursor следующего запроса (например, id последнего товара) |
page |
int | Номер отданной страницы (необязательно) |
Правила простые:
snapshot: trueдостаточно указать на первой странице — режим действует на весь обход.promotionsиpagesотдавайте только на первой странице: повторять их на каждой незачем.- Отдавать страницы по курсору надёжнее, чем по смещению: пока идёт обход,
каталог живёт своей жизнью, а сдвиг окна
OFFSETтерял бы товары. Курсор — любая ваша строка (обычно id последней позиции страницы), мы возвращаем её как есть. - Пока обход не дошёл до конца, ничего не убирается: если мы не успели забрать каталог за один заход, снимок не применяется целиком — незабранное не будет принято за «пропало». Забор продолжится следующим циклом.
- Потолок одного захода — 100 страниц по 1000 товаров и 10 минут.
Фид без has_more считается цельным — так работают все прежние интеграции,
менять их не нужно.
Товар и вариации
В каталоге ReplAI два уровня. Товар — то, что общее: название, описание, фото, бренд, категория. Вариация — конкретная позиция к продаже: цвет и размер, цена, остаток. У куртки в двух цветах и трёх размерах — один товар и шесть вариаций. Агент показывает клиенту товар целиком и уточняет, какой цвет и размер нужен.
В фиде одна запись в products — это одна вариация. Вариации одного товара
связывает общий group_id: обычно это id карточки товара на сайте. Поля
товара (name, description, images, brand, category…) повторяйте в
каждой вариации одинаково — мы берём их из последней.
Запись без group_id становится отдельным товаром с одной вариацией. Так
работают фиды, собранные до появления вариаций, — переделывать их не нужно.
Формат тела
{
"snapshot": true,
"generator": { "name": "my-shop-exporter", "version": "1.0.0" },
"products": [
{
"external_id": "42-red-M",
"group_id": "42",
"sku": "NORTH-RED-M",
"product_sku": "NORTH-WINTER",
"name": "Куртка зимняя North",
"price": "4500.00",
"old_price": "6000.00",
"special_until": "2026-08-15T23:59:59+06:00",
"quantity": 7,
"availability": "in_stock",
"color": "красный",
"size": "M",
"barcode": "4601234567890",
"brand": "North",
"description": "Мембрана 10K, утеплитель 200г.",
"category": "Одежда / Куртки",
"images": [
"https://myshop.kg/image/north-red-1.jpg",
"https://myshop.kg/image/north-red-2.jpg"
],
"url": "https://myshop.kg/kurtka-north",
"attributes": ["Мембрана 10K", "Утеплитель 200 г"],
"is_active": true
},
{
"external_id": "42-red-L",
"group_id": "42",
"sku": "NORTH-RED-L",
"product_sku": "NORTH-WINTER",
"name": "Куртка зимняя North",
"price": "4500.00",
"quantity": 0,
"availability": "backorder",
"stock_status": "Под заказ, 2-3 дня",
"color": "красный",
"size": "L",
"brand": "North",
"description": "Мембрана 10K, утеплитель 200г.",
"category": "Одежда / Куртки",
"images": ["https://myshop.kg/image/north-red-1.jpg"],
"url": "https://myshop.kg/kurtka-north"
}
],
"promotions": [
{
"external_id": "summer-sale",
"title": "Летняя распродажа",
"description": "Скидка 20% на всю обувь при заказе от 5000 сом.",
"starts_at": "2026-07-20T00:00:00+06:00",
"ends_at": "2026-08-10T23:59:59+06:00"
}
],
"pages": [
{
"external_id": "delivery",
"title": "Доставка и оплата",
"content": "Доставка по Бишкеку — бесплатно от 3000 сом...",
"url": "https://myshop.kg/delivery"
}
]
}
Все три секции опциональны — можно отдавать только товары или только акции.
generator (необязательно) — кто собрал фид: name ≤60 и version ≤20
символов. Мы показываем это владельцу магазина в кабинете; для готового
OpenCart-расширения так работает подсказка «доступно обновление».
products — до 5000 в одном ответе
Больше 5000 позиций — отдавайте страницами: суммарный размер каталога не ограничен, ограничен один ответ.
Поля вариации — у каждой свои:
| Поле | Тип | Обяз. | Описание |
|---|---|---|---|
external_id |
string ≤200 | ✅ | Стабильный технический id вариации у вас — ключ обновления: тот же id, та же вариация. Не меняйте его при смене бизнес-артикула. Этот же id получает ваш сайт, когда покупатель жмёт «В корзину» в чате |
group_id |
string ≤200 | — | Id товара, к которому относится вариация. Одинаковый у всех цветов и размеров одной карточки |
sku |
string ≤255 | — | Артикул конкретной конфигурации — цвета, размера, объёма памяти, если ваша система ведёт его отдельно. Не подставляйте сюда технический ID или артикул карточки для всех цветов/размеров. Подробнее — ниже |
price |
число/строка ≥0 | ✅ | Текущая цена продажи (по акции — уже сниженная) |
old_price |
число/строка | — | Цена «было», зачёркнутая. Должна быть выше price, иначе пара игнорируется |
special_until |
ISO-дата | — | До какого момента действует акция; без таймзоны считаем UTC |
quantity |
int ≥0 | — | Остаток. 0 — нет в наличии, если не указано «под заказ» |
availability |
string | — | Статус наличия: in_stock, backorder (под заказ), out_of_stock. Читается вместе с quantity — см. наличие и «под заказ» |
stock_status |
string ≤64 | — | Подпись статуса с вашего сайта: «Под заказ», «2-3 дня», «Предзаказ». Длиннее 64 символов — обрежьте |
color |
string ≤100 | — | Цвет |
size |
string ≤64 | — | Размер |
barcode |
string ≤50 | — | Штрихкод; по нему тоже ищется товар |
is_active |
bool | — | false — скрыть вариацию у агента. Товар скрыт, когда скрыты все его вариации |
order_ref |
объект ≤2 КБ | — | Что вернуть вашей системе в строке заказа (например, id позиции в учёте). Храним как есть |
Поля товара — одинаковые у всех вариаций с одним group_id:
| Поле | Тип | Обяз. | Описание |
|---|---|---|---|
name |
string ≤500 | ✅ | Название товара — без цвета и размера, они в полях вариации |
product_sku |
string ≤255 | — | Артикул карточки — наименования или модели, общий для всех её вариаций. Может отличаться от sku конкретной вариации |
description |
string ≤8000 | — | Описание для агента. Хватает 1500–2000 символов: в поиск попадает начало текста, а клиенту агент пересказывает своими словами — длинные простыни характеристик только раздувают фид |
category |
string ≤255 | — | Категория; создаётся автоматически. Два уровня — через « / »: Одежда / Куртки |
brand |
string ≤100 | — | Бренд |
warranty |
string ≤50 | — | Гарантия: 1 год |
attributes |
массив строк, до 20 | — | Короткие характеристики: Мембрана 10K |
images |
массив URL, до 10 | — | Фото товара, главное первым. Адреса должны открываться из интернета |
url |
string ≤2048 | — | Страница товара на вашем сайте, полный адрес https://… (или http://…). Фото и название карточки товара в чате открывают её. Относительный путь (/product/42) или другой неверный адрес не ломает загрузку — товар просто будет без ссылки |
Наличие и «под заказ»
Остаток решает первым. quantity больше нуля — позиция в наличии, что бы
ни стояло в статусе. При quantity: 0:
availability |
Что скажет агент |
|---|---|
backorder |
Под заказ: так и скажет, может назвать вашу подпись из stock_status («2-3 дня») и предложит оформить заказ через менеджера магазина |
out_of_stock, in_stock или поля нет |
Нет в наличии |
Заказ на позицию под заказ агент сам не оформляет — как и на любую позицию с нулевым остатком: её подтверждает ваш менеджер. Когда клиент спрашивает, что есть в наличии, агент видит и позиции под заказ, но не выдаёт их за наличие.
Оба поля — текущее состояние, как остаток: каждая запись заменяет прежнее
значение. Запись без них означает «статус не указан», поэтому «под заказ» не
останется висеть, если вы перестали его передавать. Других значений
availability, кроме трёх перечисленных, не передавайте; нет статуса —
просто не передавайте поле.
Технический ID и настоящий артикул
external_id отвечает за обновление существующей вариации, а sku и
product_sku — за бизнес-артикулы. Смена артикула при прежнем external_id
обновляет запись, не создаёт новый товар и не меняет ссылку order_ref
старого заказа.
product_sku— артикул карточки: наименования или модели, общий для всех её цветов и размеров. По нему агент находит товар, когда клиент называет артикул из каталога или с ценника.sku— артикул конкретной конфигурации, если в вашей системе он свой у каждого цвета, размера или объёма памяти. Если отдельных артикулов у конфигураций нет, передавайтеsku: "", а не артикул карточки в каждой вариации. У товара без цветов и размеровskuобычно совпадает сproduct_sku.
Отсутствие поля или null значит, что источник это поле не передаёт: уже
сохранённый артикул остаётся прежним, так старые фиды сохраняют совместимость.
Новая вариация без sku заводится без артикула — технический
external_id в артикул не копируется. Явная пустая строка "" означает
«артикула нет» и очищает ранее переданный. Пробелы по краям убираются;
регистр, ведущие нули и буквенно-цифровые коды сохраняются.
Раньше вариация без артикула получала артикулом свой external_id. Такие
сохранённые значения не переписываются, но считаются «артикула нет»: агент
их не показывает, а при совпадении с настоящим артикулом другой записи они
ему уступают. Это касается только значений, скопированных прежним правилом.
Явно переданный sku — настоящий артикул, даже если он совпадает с
external_id записи: агент его показывает и находит по нему товар.
Если артикул уже занят
Конфликт артикула никогда не останавливает обновление цены и остатка.
skuзаписи уже есть у другой вариации в каталоге этого агента. Если там он технический (когда-то скопирован из еёexternal_id, а не передан явно) или та вариация скрыта, артикул переходит к новой записи, а у прежней очищается. Если там это настоящий артикул активной вариации — запись применяется без артикула, а в результате забора появляется пометкаbusiness_sku_taken.- Записи одного
group_idв одном ответе фида передают разныеproduct_sku— карточка применяется без артикула карточки, пометкаconflicting_product_sku.
Пометка — не пропуск: цена, остаток и остальные поля записи обновлены. Проверьте артикулы в своей системе — два товара с одним артикулом обычно означают опечатку или дубль карточки.
В OpenCart 3.x артикул есть у карточки (product.sku), но у стандартных
значений цвета/размера собственного артикула нет. Плагин с версии 1.4.2
передаёт product_sku во всех записях этой карточки, sku — у её базовой
вариации без осей цвета/размера. У выбираемых сочетаний он передаёт sku: "",
сохраняя их прежние external_id и order_ref. Если ваш модуль хранит
настоящие артикулы сочетаний, нужен экспорт именно из его данных. С версии
1.4.3 плагин передаёт и статус наличия: при нулевом остатке — по статусу,
который карточка OpenCart показывает на сайте.
Товар и его варианты содержат товарные характеристики. Условия оплаты,
кредита, доставки и услуги магазина передавайте в pages — в Базу знаний,
не в sku, barcode, размер или варианты товара. Защитное стекло и другие
аксессуары — самостоятельные карточки со своими артикулами, ценами и остатками.
promotions — до 200 в одном фиде
Акции попадают в Базу знаний агента, к которому подключён фид: он
рассказывает условия словами («скидка 20% на обувь до 10 августа»). Точные
акционные цены конкретных товаров передавайте через old_price /
special_until товара.
| Поле | Тип | Обяз. | Описание |
|---|---|---|---|
external_id |
string ≤200 | — | Стабильный id акции (иначе ключ — от названия) |
title |
string ≤400 | ✅ | Название акции |
description |
string ≤8000 | — | Условия |
starts_at, ends_at |
ISO-дата | — | Период. Акция с истёкшим ends_at игнорируется |
pages — до 500 в одном фиде
Информационные страницы сайта (доставка, оплата, гарантия, о магазине) — тоже в Базу знаний агента, к которому подключён фид: он отвечает на вопросы клиентов по их содержимому. Другие агенты организации этих страниц не видят.
| Поле | Тип | Обяз. | Описание |
|---|---|---|---|
external_id |
string ≤200 | — | Стабильный id страницы (иначе ключ — от URL/названия) |
title |
string ≤400 | ✅ | Заголовок |
content |
string ≤100000 | ✅ | Текст страницы (без HTML-тегов) |
url |
string ≤1000 | — | Адрес страницы — агент даст клиенту ссылку |
Результат забора
Сводку по последнему забору кабинет показывает сразу после подключения и после кнопки «Обновить сейчас»:
{
"products_received": 120,
"products_created": 3,
"products_updated": 117,
"products_deactivated": 2,
"products_skipped": 0,
"skipped_reasons": [],
"knowledge_created": 1,
"knowledge_updated": 0,
"knowledge_unchanged": 4,
"knowledge_deleted": 0,
"knowledge_skipped": 0,
"pages_fetched": 1,
"truncated": false
}
products_* считают вариации: одна запись фида — одна вариация.
В skipped_reasons попадают коды двух видов. Пропуск — запись не
применена, её считает products_skipped. Пометка — запись применена,
но одно поле отброшено или не записано; в products_skipped она не входит.
Пропуски:
duplicate_external_id— повторныйexternal_idв одном снимке: применяется первая запись.sku_taken_by_other_source— такойexternal_idуже принадлежит вариации другого источника: заведённой вручную, пришедшей из Stores Manager или другого фида. Чужую вариацию фид не перезаписывает — дайте записи свойexternal_id. Название кода историческое: речь оexternal_id, а не об артикулеsku.plan_limit_reached— каталог агента достиг лимита тарифа, поэтому новые товары сверх него не заводятся. Лимит у каждого агента свой и считает товары, а не вариации: новый размер уже загруженного товара лимит не тратит. Цены и остатки по уже загруженным товарам при этом обновляются как обычно, а в кабинет приходит уведомление с числом незагруженных позиций — лимиты по тарифам перечислены на странице цен.card_too_large— товар вырос бы больше, чем помещается в ответ поиска: свыше 500 вариаций или 512 КБ данных. Запись не применена, товар остаётся в прежнем виде. Разделите такой товар на несколько карточек.unsupported_item— запись прошла проверку формата, но сохранить её нельзя: пустые после обрезкиexternal_idилиname, символ NUL в тексте, цена больше 9 999 999 999,99, остаток вне диапазона целых чисел, адрес фото длиннее 4096 байт, характеристика длиннее 2000 байт, дата вне 0001–9999 годов. Остальные записи применяются.
Пометки:
old_price_not_above_price— пара акции отброшена, товар сохранён поprice.business_sku_taken—skuзаписи занят настоящим артикулом другой активной вариации: запись применена без артикула (подробнее).conflicting_product_sku— вариации одногоgroup_idпередали разныеproduct_sku: карточка применена без артикула карточки.reserved_payment_barcode_dropped— вbarcodeпришла подпись условия оплаты («Кредит», «Рассрочка», в том числе «через банк»), а не штрихкод: запись применена без штрихкода. Условия оплаты передавайте вpages.
pages_fetched — сколько страниц забрали за этот заход, truncated: true —
обход не дошёл до конца (кнопка «Обновить сейчас» ждёт не дольше 40 секунд,
чтобы не держать кабинет). Забранное применено, остальное доберёт ближайший
цикл; ничего при этом не убирается.
Если фид недоступен или не проходит проверку формата, забор прерывается и причина попадает в кабинет строкой «Последняя ошибка» — каталог при этом остаётся прежним.
Проверка фида
Перед тем как вставлять адрес в кабинет, убедитесь, что он отдаёт валидный JSON и открывается снаружи:
curl -s "https://мой-магазин.kg/replai-feed.json" | head -c 400
Правила и советы
snapshot: trueубирает только вариации, ранее пришедшие из этого фида; заведённые вручную или из других интеграций не трогаются. То же с Базой знаний — удаляются только фидовые записи.- Пустой
productsприsnapshot: trueуберёт весь фидовый каталог — не отдавайте снимок, если каталог не собрался. - Один товар в разных цветах и размерах — отдельные записи с разными
external_id(например42-red-M,42-red-L) и общимgroup_id(42). external_idстабилен и уникален во всём каталоге агента, включая ручные варианты и другие источники. Настоящий артикул вариацииskuне должен принадлежать другой вариации того же каталога; технический ID источника и бизнес-артикул — разные поля.- Остаток 0 и
availability: "backorder"— «под заказ», остаток 0 без статуса — «нет в наличии». Положительный остаток всегда «в наличии». - Держите один ответ ≤5000 позиций и ≤25 МБ; каталог больше — отдавайте страницами.