Каталог на сайте
ReplAI Feed — формат фида каталога
JSON-контракт для передачи товаров, акций и информационных страниц агенту ReplAI. Подходит для любого стека.
Универсальный способ передать AI-агенту ReplAI товары, акции и информационные страницы вашего сайта. Один контракт для любого стека: PHP, Python, Node, 1С-выгрузка или скрипт по расписанию.
Как это работает
Ничего отправлять не нужно: вы отдаёте JSON по постоянному URL, а ReplAI сам забирает его каждые 30 минут. Cron на вашем хостинге не требуется.
- Настройте на сайте страницу, отдающую фид, — например
https://мой-магазин.kg/replai-feed.json. - В кабинете ReplAI: Агент → Интеграции → Фид каталога → Другой сайт — вставьте адрес в поле «Ссылка на фид вашего сайта» и нажмите «Сохранить URL».
- Нажмите «Забрать сейчас» — сразу увидите, сколько товаров и записей знаний загрузилось. Ошибка последнего забора тоже видна в кабинете.
Ответ трактуется как полный снимок (snapshot: true), если в JSON явно не
указано иное.
Требования к URL: http(s), без логина/пароля в адресе, доступен из интернета
(не localhost и не внутренний IP), без редиректов — давайте конечный адрес.
Один ответ — до 25 МБ; каталог, который в этот размер не влезает, отдавайте
страницами. Если фид «секретный», добавьте свой
токен в query — мы сохраним адрес как есть.
Режим обновления
Рекомендуемый режим — полный снимок ("snapshot": true): отдавайте по URL
весь актуальный каталог, всё пропавшее из снимка скрывается автоматически. Это
же значение подставляется по умолчанию, если поля в ответе нет.
Частичная выдача ("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 считается цельным — так работают все прежние интеграции,
менять их не нужно.
Формат тела
{
"snapshot": true,
"generator": { "name": "my-shop-exporter", "version": "1.0.0" },
"products": [
{
"external_id": "42-red-M",
"name": "Куртка зимняя North",
"price": "4500.00",
"old_price": "6000.00",
"special_until": "2026-08-15T23:59:59+06:00",
"quantity": 7,
"color": "красный",
"size": "M",
"description": "Мембрана 10K, утеплитель 200г.",
"category": "Куртки",
"is_active": true
}
],
"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 — тот же товар) |
name |
string ≤500 | ✅ | Название |
price |
число/строка ≥0 | ✅ | Текущая цена продажи (по акции — уже сниженная) |
old_price |
число/строка | — | Цена «было», зачёркнутая. Должна быть выше price, иначе пара игнорируется |
special_until |
ISO-дата | — | До какого момента действует акция; без таймзоны считаем UTC |
quantity |
int ≥0 | — | Остаток (0 = нет в наличии) |
color, size |
string | — | Вариация |
description |
string ≤8000 | — | Описание для агента. Хватает 1500–2000 символов: в поиск попадает начало текста, а клиенту агент пересказывает своими словами — длинные простыни характеристик только раздувают фид |
category |
string ≤255 | — | Название категории; создаётся автоматически |
is_active |
bool | — | false — скрыть товар у агента |
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
}
skipped_reasons: duplicate_external_id — повторный id в одном фиде;
old_price_not_above_price — пара акции отброшена (товар сохранён).
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). - Держите один ответ ≤5000 позиций и ≤25 МБ; каталог больше — отдавайте страницами.