Shopping Assistant API
На этой странице описаны контракты Shopping Assistant API: отправка сообщений, обработка структурированного ответа, восстановление истории и tracking товарных подборок.
Перед работой с endpoint-ами выполните шаги из гайда по API-интеграции. Настройка авторизации, пользователей, контекста и API-кампании выполняется через Gravity Field API V2.
POST /shopping/generate
POST https://shopping-assistant-api.gravityfield.ai/shopping/generate
Content-Type: application/json
Endpoint принимает новое сообщение пользователя и возвращает текстовые блоки, товарные карточки, кнопки действий и быстрые ответы. Готовый UI чата API не возвращает.
Для всех сообщений одного диалога используйте одинаковые threadId и resourceId. В resourceId передавайте user.uid, полученный через Gravity Field API V2. Историю диалога повторно отправлять не нужно: Shopping Assistant сохраняет контекст по этим идентификаторам.
Для обычной интеграции используйте роль user.
Контракт запроса
type ShoppingTextContentPart = {
type: "text";
text: string;
};
type ShoppingImageContentPart = {
type: "image";
source:
| {
type: "url";
url: string;
}
| {
type: "base64";
mediaType: "image/jpeg" | "image/png" | "image/webp";
data: string;
};
};
type ShoppingGenerateRequest = {
threadId: string;
resourceId: string;
trafficType?: "real" | "test";
messages: Array<{
role: "user";
content:
| string
| Array<ShoppingTextContentPart | ShoppingImageContentPart>;
}>;
runtimeContext: {
tenant_id: string;
ctx?: {
type?: string;
data?: unknown;
lng?: string | null;
location?: string | null;
};
};
};
Поля запроса
Как передать изображение
Чтобы отправить изображение, передайте в content массив блоков. В одном пользовательском сообщении поддерживается только одно изображение. Вместе с ним можно передать текстовый блок с пояснением:
{
"threadId": "thread-8f2b1c",
"resourceId": "665f0a000000000000000001",
"trafficType": "test",
"messages": [
{
"role": "user",
"content": [
{
"type": "text",
"text": "Найдите похожий диван в более светлом цвете"
},
{
"type": "image",
"source": {
"type": "base64",
"mediaType": "image/jpeg",
"data": "/9j/4AAQSkZJRgABAQ..."
}
}
]
}
],
"runtimeContext": {
"tenant_id": "670ccaae56afcafaf808c146"
}
}
В поле data передавайте только Base64-содержимое файла, без префикса data:image/jpeg;base64,. Значение mediaType должно соответствовать фактическому формату файла.
Изображение также можно передать по URL. Замените блок image в примере выше:
{
"type": "image",
"source": {
"type": "url",
"url": "https://cdn.example.com/user-photo.webp"
}
}
Текст необязателен: для запроса только по изображению передайте в content массив с одним блоком image.
Ограничения для изображения
После проверки Shopping Assistant учитывает EXIF-ориентацию, уменьшает изображение до размера не более 2048 × 2048 пикселей без увеличения и преобразует его в JPEG для обработки.
Как заполнять ctx
runtimeContext.ctx использует модель контекста API V2. Передавайте актуальный контекст, согласованный с /visit и /choose. Поддерживаемые типы и формат data описаны в разделе Контекст API V2.
Если чат открыт на карточке товара, передайте SKU товара в контексте:
{
"runtimeContext": {
"tenant_id": "670ccaae56afcafaf808c146",
"ctx": {
"type": "PRODUCT",
"data": ["1066109"]
}
}
}
Минимальный пример
Запрос
curl --request POST \
--url 'https://shopping-assistant-api.gravityfield.ai/shopping/generate' \
--header 'Content-Type: application/json' \
--data '{
"threadId": "thread-8f2b1c",
"resourceId": "665f0a000000000000000001",
"trafficType": "test",
"messages": [
{
"role": "user",
"content": "Помогите выбрать корм для взрослой кошки"
}
],
"runtimeContext": {
"tenant_id": "670ccaae56afcafaf808c146"
}
}'
Ответ
{
"threadId": "thread-8f2b1c",
"resourceId": "665f0a000000000000000001",
"response": {
"contents": [
{
"type": "message",
"value": "Подберу корм, но сначала уточню пару деталей. Кошка стерилизована и есть ли особенности по здоровью?"
}
],
"ui": {
"suggests": [
"Стерилизована, без особенностей",
"Не стерилизована, чувствительное пищеварение",
"Пожилая кошка, нужен мягкий корм"
]
}
}
}
Пример ответа с товарами
{
"threadId": "thread-8f2b1c",
"resourceId": "665f0a000000000000000001",
"response": {
"contents": [
{
"type": "message",
"value": "Вот несколько подходящих вариантов. Смотрите на возраст, стерилизацию и чувствительность пищеварения."
},
{
"type": "products",
"title": "Подходящие корма для взрослых кошек",
"value": [
{
"sku": "1066109",
"name": "Сухой корм для взрослых кошек",
"price": 1299,
"features": "Для взрослых кошек; повседневный рацион",
"is_stm": false,
"brand": "Demo Brand",
"url": "https://example.com/product/1066109",
"image_url": "https://example.com/product/1066109.jpg",
"events": [
{
"type": "visible_impression",
"urls": [
"https://evs-01.gravityfield.ai/v2/engagement?...",
"https://evs-02.gravityfield.ai/v2/engagement?..."
]
},
{
"type": "click",
"urls": [
"https://evs-01.gravityfield.ai/v2/engagement?..."
]
}
]
},
{
"sku": "1023814",
"name": "Корм для стерилизованных кошек",
"price": 1599,
"features": "Для стерилизованных кошек; контроль веса",
"is_stm": true
}
]
},
{
"type": "message",
"value": "Если кошка стерилизована, лучше начать со второго варианта. Если нет, подойдет первый."
}
],
"ui": {
"suggests": [
"Показать варианты дешевле",
"Нужен влажный корм",
"У кошки чувствительное пищеварение"
]
}
},
"events": [
{
"type": "WRIMP",
"urls": [
"https://evs-01.gravityfield.ai/v2/engagement?...",
"https://evs-02.gravityfield.ai/v2/engagement?..."
]
}
]
}
Контракт ответа
type ShoppingGenerateResponse = {
threadId: string;
resourceId: string;
response: {
contents: Array<
| { type: "message"; value: string }
| { type: "products"; value: Product[]; title?: string }
| { type: "button"; value: string; action: string }
>;
ui?: {
suggests: string[];
};
};
events?: EngagementEvent[];
};
type Product = {
sku?: string;
name: string;
price?: number | null;
features?: string;
is_stm?: boolean;
brand?: string;
url?: string;
image_url?: string;
events?: EngagementEvent[];
};
type EngagementEvent = {
type: string;
urls: string[];
};
Для кнопок клиентская сторона должна обработать action. Например, open_support_chat можно связать с открытием клиентского чата поддержки.
Поле title в блоке products содержит готовый заголовок для этой группы рекомендаций. Если поле пришло, отобразите его над товарными карточками. Если title отсутствует, блок можно отобразить без заголовка.
Для товарных карточек считайте sku основным идентификатором товара. Поля price, brand, url, image_url и другие данные могут использоваться для быстрого отображения карточки, но актуализация цены, изображения, наличия, открытие PDP и добавление в корзину остаются на стороне клиентского канала или клиентского каталога.
POST /shopping/history
Чтобы восстановить сообщения после перезагрузки страницы или повторного открытия чата, вызовите:
POST https://shopping-assistant-api.gravityfield.ai/shopping/history
Content-Type: application/json
Endpoint возвращает только публичные пользовательские сообщения и ответы Shopping Assistant. Внутренние system-сообщения, вызовы инструментов и служебные данные в ответ не попадают.
Запрос истории
curl --request POST \
--url 'https://shopping-assistant-api.gravityfield.ai/shopping/history' \
--header 'Content-Type: application/json' \
--data '{
"tenant_id": "670ccaae56afcafaf808c146",
"resourceId": "665f0a000000000000000001",
"threadId": "thread-8f2b1c",
"page": 1,
"limit": 50
}'
История доступна только при точном совпадении tenant_id, resourceId и threadId. Если диалог не найден или не принадлежит указанной секции или пользователю, API возвращает одинаковый ответ 404.
Ответ
{
"threadId": "thread-8f2b1c",
"resourceId": "665f0a000000000000000001",
"items": [
{
"id": "shopping:user:4f8c...",
"role": "user",
"content": "Помогите выбрать корм для взрослой кошки",
"createdAt": "2026-07-28T10:00:00.000Z"
},
{
"id": "shopping:assistant:8a21...",
"role": "assistant",
"createdAt": "2026-07-28T10:00:01.250Z",
"threadId": "thread-8f2b1c",
"resourceId": "665f0a000000000000000001",
"response": {
"contents": [
{
"type": "message",
"value": "Кошка стерилизована и есть ли особенности по здоровью?"
}
],
"ui": {
"suggests": [
"Стерилизована, без особенностей",
"Не стерилизована",
"Есть особенности по здоровью"
]
}
},
"responseLatencyMs": 1250
}
],
"pagination": {
"page": 1,
"limit": 50,
"hasMore": false
}
}
Элементы в items отсортированы по времени создания от ранних к поздним в пределах страницы:
- для
role: "user"полеcontentимеет тот же формат, что в запросе/shopping/generate: строка для текста или массив блоковtextиimage; - для
role: "assistant"полеresponseимеет тот же контракт, что ответ/shopping/generate; дополнительно могут присутствоватьresponseLatencyMsиevents; - если
pagination.hasMoreравноtrue, запросите следующую страницу, увеличивpageна1.
Для сообщения с изображением history endpoint возвращает блок image с Base64. Для отображения превью соберите data URL из source.mediaType и source.data, но не передавайте это сообщение повторно в /shopping/generate.
const previewUrl =
`data:${image.source.mediaType};base64,${image.source.data}`;
Ответ содержит заголовок Cache-Control: no-store. Не сохраняйте историю в общем или публичном кеше на клиентской стороне.
Ошибки получения истории
Tracking из ответа Shopping Assistant
В ответе Shopping Assistant tracking-события могут находиться на двух уровнях:
При наступлении события обработайте все элементы events[] с соответствующим type. Для каждого найденного элемента выполните GET-запрос по каждому URL из urls[]. Не ограничивайтесь первым элементом events[] или первым URL: один факт взаимодействия может требовать нескольких tracking-запросов.
async function sendTrackingEvents(events, type) {
const urls = events
.filter((event) => event.type === type)
.flatMap((event) => event.urls);
await Promise.allSettled(
urls.map((url) => fetch(url, { method: "GET", keepalive: true }))
);
}
// Весь рекомендательный блок стал видимым.
sendTrackingEvents(result.events ?? [], "WRIMP");
// Стала видимой конкретная карточка товара.
sendTrackingEvents(product.events ?? [], "visible_impression");
Отправляйте событие видимого показа один раз для одного фактического показа блока или карточки. Ошибка tracking-запроса не должна блокировать отображение рекомендаций или действие пользователя. Общие правила работы с tracking URL описаны в разделе Передача взаимодействий пользователя с кампаниями.
Ошибки /shopping/generate и повтор запроса
Рекомендуем показывать индикатор загрузки до получения ответа, не создавать новый threadId при повторе того же пользовательского сообщения и защищаться от двойной отправки одинакового сообщения при нестабильной сети.