Перейти к содержимому
ReplAI
Разделы документации

Каталог на сайте

ReplAI Feed — формат фида каталога

JSON-контракт для передачи товаров, акций и информационных страниц агенту ReplAI. Подходит для любого стека.

Универсальный способ передать AI-агенту ReplAI товары, акции и информационные страницы вашего сайта. Один контракт для любого стека: PHP, Python, Node, 1С-выгрузка или скрипт по расписанию.

Как это работает

Ничего отправлять не нужно: вы отдаёте JSON по постоянному URL, а ReplAI сам забирает его каждые 30 минут. Cron на вашем хостинге не требуется.

  1. Настройте на сайте страницу, отдающую фид, — например https://мой-магазин.kg/replai-feed.json.
  2. В кабинете ReplAI: Товары → Источники → Товары с вашего сайта → Подключить, вкладка «Другой сайт». Вставьте адрес в поле «Ссылка на выгрузку товаров» и нажмите «Подключить» — товары загрузятся сразу, и вы увидите, сколько их пришло. Ошибка последнего забора тоже видна в кабинете.
  3. Дальше каталог обновляется сам; забрать изменения раньше срока — кнопка «Обновить сейчас» в том же окне.

Ответ трактуется как полный снимок (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 МБ; каталог больше — отдавайте страницами.