Оценка ответа

Сохраните оценку конкретного сообщения ассистента. GET подходит для готовых ссылок из ответа генерации; PUT также позволяет снять реакцию. Оценки покупателей отображаются отдельно от отзывов команды в личном кабинете.

Для гостевого ресурса оба метода требуют Authorization: Bearer gst_.... Используйте PUT с этим заголовком: обычный переход по GET-ссылке не передаёт токен. Примеры без заголовка ниже относятся к прежнему режиму. Правила передачи токена и общие ошибки: Гостевые токены.

Оба метода возвращают 404, если диалог скрыт через «Убрать из истории». Не сохраняйте реакцию в UI как отправленную при таком ответе.

GET /shopping/feedback

После сохранения ответа генерация добавляет в корневой events[] ссылки двух типов:

{
  "events": [
    {
      "type": "like",
      "url": "https://shopping-assistant-api.gravityfield.ai/shopping/feedback?tenant_id=demo-section&resourceId=demo-user&threadId=demo-thread&messageId=demo-answer&reaction=like"
    },
    {
      "type": "dislike",
      "url": "https://shopping-assistant-api.gravityfield.ai/shopping/feedback?tenant_id=demo-section&resourceId=demo-user&threadId=demo-thread&messageId=demo-answer&reaction=dislike"
    }
  ]
}

Идентификаторы в примере условные. Используйте готовый url из фактического ответа: тело запроса не требуется. В прежнем режиме дополнительный токен не нужен; для гостевого ресурса обязателен заголовок Authorization.

  1. Найдите событие нужного типа рядом с сохранённым ответом.
  2. Вызовите его URL методом GET только при нажатии соответствующей кнопки.
  3. После 200 обновите состояние оценки; при ошибке сохраните предыдущее состояние.
async function rateAnswer(result, reaction) {
  const event = result.events?.find(item => item.type === reaction);
  if (!event?.url) throw new Error('Для ответа нет ссылки оценки');
  const response = await fetch(event.url, { method: 'GET', cache: 'no-store' });
  if (!response.ok) throw new Error('Не удалось сохранить оценку');
  return response.json();
}
// В обработчике кнопки «Нравится»: rateAnswer(result, 'like').

Не вызывайте все URL из events[] при получении ответа: like и dislike меняют состояние одного сообщения. Повторная реакция не создаёт дубликат, противоположная заменяет предыдущую.

Обязательные query-параметры: непустые tenant_id, resourceId, threadId, messageId и reaction: like | dislike. GET не принимает null; для снятия оценки используйте PUT. Все готовые ссылки ведут на production-домен, включая ссылки из другого окружения. Ответ должен существовать в принимающем сервисе.

HTTP Результат
200 { "reaction": "like", "updatedAt": "2026-10-13T10:00:00.000Z" }
400 Invalid request query: проверьте параметры.
404 Нет сохранённого ответа с указанной связкой, диалог скрыт или принадлежит другой гостевой сессии.
500 Failed to save feedback: предложите повторить позже.

В прежнем режиме GET не ограничивается списком разрешённых IP секции. Для гостевого ресурса проверяются токен и IP-ограничения. Сервер проверяет принадлежность сообщения указанным секции, пользователю и диалогу; ссылка не подтверждает личность отправителя. Ответы используют Cache-Control: no-store.

Debug-ответы не сохраняются и не получают feedback-ссылки. History API не добавляет эти ссылки: для восстановленного ответа используйте PUT и id сообщения. Готовый Web-виджет 1.42 также использует PUT.

PUT /shopping/feedback

Чтобы собрать оценку конкретного ответа Shopping Assistant, вызовите:

PUT https://shopping-assistant-api.gravityfield.ai/shopping/feedback
Content-Type: application/json

Endpoint сохраняет текущую реакцию пользователя на сообщение ассистента. Повторная отправка того же состояния идемпотентна: она не создает дубликат оценки.

Запрос

curl --request PUT \
  --url 'https://shopping-assistant-api.gravityfield.ai/shopping/feedback' \
  --header 'Content-Type: application/json' \
  --data '{
    "tenant_id": "demo-section",
    "resourceId": "demo-user",
    "threadId": "thread-8f2b1c",
    "messageId": "shopping:assistant:8a21...",
    "reaction": "like"
  }'
Поле Обязательное Назначение
tenant_id Да ID секции Gravity Field. Должен совпадать с runtimeContext.tenant_id запроса /shopping/generate.
resourceId Да Тот же идентификатор, что в генерации: user.uid Gravity Field или используемый в интеграции user.custom.
threadId Да ID диалога, в котором был получен ответ.
messageId Да Значение assistantMessageId из ответа /shopping/generate или id сообщения ассистента из /shopping/history.
reaction Да like для положительной оценки, dislike для отрицательной или null, чтобы удалить ранее сохраненную оценку.

API принимает оценку только при точном совпадении tenant_id, resourceId, threadId и messageId. Это не позволяет записать реакцию на сообщение из другого диалога или профиля пользователя.

Ответ

{
  "reaction": "like",
  "updatedAt": "2026-08-07T10:00:00.000Z"
}

В reaction возвращается сохраненное состояние. Поле updatedAt содержит время его последнего изменения.

Чтобы заменить оценку, отправьте тот же запрос с другим значением reaction. Чтобы пользователь мог снять выбранную оценку, отправьте reaction: null.

Ошибки отправки оценки

HTTP-статус Причина Что делать клиенту
400 Некорректный JSON, отсутствует обязательное поле, передано пустое значение идентификатора или неподдерживаемая реакция. Проверить payload перед повторной отправкой.
403 IP не разрешён для секции. Проверить условия доступа с командой Gravity Field.
404 Сообщение ассистента не найдено, диалог скрыт или принадлежит другой гостевой сессии, либо связка идентификаторов не совпадает. Не сохранять оценку локально как отправленную; проверить идентификаторы сообщения, диалога и сессии.
500 Оценку не удалось сохранить. Вернуть элемент управления в предыдущее состояние и предложить повторить запрос.

Проверьте положительную, отрицательную и повторную оценку, смену реакции и снятие через PUT. Диагностика: Оценка не сохраняется.