# Гостевые токены

Гостевой токен используется в запросах Shopping Assistant для конкретной секции и пользователя Gravity Field. В `resourceId` передавайте `user.uid`, а если ваша интеграция использует `user.custom` — его значение. Клиент регистрирует гостевую сессию с этим идентификатором, сохраняет токен, затем передаёт его в заголовке `Authorization`.

Существующие интеграции продолжают работать в прежнем режиме. Для `resourceId`, зарегистрированного через гостевую сессию, токен обязателен: убрать заголовок и продолжить запросы без него нельзя. Гостевой токен не подтверждает вход покупателя в аккаунт магазина.

## Получение токена

### `POST /shopping/guest-sessions`

Перед первым запросом генерации получите идентификатор пользователя через [базовую интеграцию Gravity Field API V2](/Integration/api_integration/v2/getting_started.md). Используйте тот же идентификатор в Shopping Assistant:

| Идентификация в Gravity Field | Значение `resourceId` |
| --- | --- |
| Через `user.uid` | Значение `user.uid`, полученное от Gravity Field. В Web — `GF.userData.uid`. |
| Через `user.custom` | Внешний ID, который ваша интеграция передаёт в `user.custom`. В запросах API V2 продолжайте передавать и `user.ses`. |

Сохраняйте выбранный способ идентификации при регистрации токена и во всех последующих запросах ассистента. Не создавайте отдельный UUID для Shopping Assistant и не используйте общую заглушку вроде `"api request"` для разных покупателей.

Текущий метод выдаёт токен только до первого использования этого `resourceId` в Shopping Assistant. Если ресурс уже зарегистрирован или для него существует история, API возвращает `409 RESOURCE_ALREADY_EXISTS`. Повторная выдача токена и перевод существующего ресурса на гостевые токены этим методом не поддерживаются.

В примере ниже замените `demo-section` на ID подключённой секции, а `USER_UID` — на `user.uid` или используемый в вашей интеграции `user.custom`.

```bash
curl --request POST \
  --url 'https://shopping-assistant-api.gravityfield.ai/shopping/guest-sessions' \
  --header 'Content-Type: application/json' \
  --data '{
    "tenant_id": "demo-section",
    "resourceId": "USER_UID"
  }'
```

| Поле | Обязательное | Назначение |
| --- | --- | --- |
| `tenant_id` | Да | ID секции, для которой подключён Shopping Assistant. |
| `resourceId` | Да | `user.uid` Gravity Field или значение `user.custom`, если интеграция использует внешний ID. |

Оба поля — непустые строки длиной до 512 символов. Дополнительные поля не принимаются. Для регистрации действуют IP-ограничения секции.

### Ответ

API возвращает `201 Created` и `Cache-Control: no-store`:

```json
{
  "sessionId": "c7cf5ab3-49e0-4425-9ef3-0d68c29ee739",
  "accessToken": "gst_...",
  "tokenType": "Bearer",
  "resourceId": "USER_UID",
  "expiresAt": "2026-11-15T10:00:00.000Z"
}
```

Значение `accessToken` в примере сокращено. Настоящий токен состоит из префикса `gst_` и 43 символов Base64url.

| Поле | Назначение |
| --- | --- |
| `sessionId` | ID созданной сессии. Не заменяет токен в запросах. |
| `accessToken` | Секретный токен. Возвращается только при регистрации. |
| `tokenType` | Схема передачи токена: `Bearer`. |
| `resourceId` | Идентификатор, переданный при регистрации. |
| `expiresAt` | Срок действия на момент создания сессии, в ISO 8601. |

Сохраните `accessToken` вместе с `resourceId` и ID секции. При параллельных запросах используйте одну сохранённую сессию. Не записывайте токен в URL, аналитику, error tracking или прикладные логи.

## Передача токена

В последующих запросах сохраняйте зарегистрированный `resourceId` и добавляйте заголовок:

```http
Authorization: Bearer gst_...
```

В примере генерации переменная окружения `GUEST_ACCESS_TOKEN` содержит полное значение `accessToken` из ответа регистрации. Используйте свою секцию и зарегистрированный `resourceId`:

```bash
curl --request POST \
  --url 'https://shopping-assistant-api.gravityfield.ai/shopping/generate' \
  --header 'Content-Type: application/json' \
  --header "Authorization: Bearer $GUEST_ACCESS_TOKEN" \
  --data '{
    "resourceId": "USER_UID",
    "threadId": "thread-8f2b1c",
    "trafficType": "test",
    "messages": [
      {
        "id": "message-1001",
        "role": "user",
        "content": "Помогите выбрать корм для взрослой кошки"
      }
    ],
    "runtimeContext": {
      "tenant_id": "demo-section"
    }
  }'
```

Для гостевого ресурса токен требуется в следующих методах:

| Метод | Контракт |
| --- | --- |
| `POST /shopping/generate`, включая debug-режим и повтор запроса | [Генерация ответа](generate.md) |
| `POST /shopping/history` | [История диалога](history.md) |
| `GET /shopping/dialogs` и `DELETE /shopping/dialogs/{threadId}` | [Список и скрытие диалогов](dialogs.md) |
| `GET /shopping/feedback` и `PUT /shopping/feedback` | [Оценка ответа](feedback.md) |
| `POST /dialogs/share` и `POST /dialogs/share/revoke` | [Шаринг диалогов](dialog_sharing.md) |

Для оценки в гостевой интеграции используйте PUT с заголовком `Authorization`. Обычный переход по GET-ссылке не передаёт этот заголовок. Не добавляйте токен в query-параметры. В API шаринга `user_id` должен совпадать с зарегистрированным `resourceId`; `tenant_id` в тело запроса не добавляют.

Чтение публичной копии через `GET /dialogs/shared/{share_token}` использует отдельный `share_token` и не требует гостевого токена. Ключ API V2 также не заменяет гостевой токен.

## Срок действия

Сессия действует 30 дней бездействия. После успешного авторизованного обращения сервер пытается продлить её ещё на 30 дней. Ошибочные запросы срок не продлевают; истёкшая или отозванная сессия не возобновляется. Поле `expiresAt` из ответа регистрации содержит первоначальный срок, а не обновляемую дату. Отдельного refresh token нет.

Если токен потерян, истёк или отозван, текущий метод не позволяет получить новый токен для того же `resourceId`. Покажите в интерфейсе, что сессия недоступна. Не меняйте `user.uid`, `user.custom` или `resourceId` ради повторной регистрации: идентификация должна оставаться общей с Gravity Field.

## Отзыв токена

### `POST /shopping/guest-sessions/revoke`

Передайте текущий гостевой токен в заголовке. Тело запроса не требуется:

```bash
curl --request POST \
  --url 'https://shopping-assistant-api.gravityfield.ai/shopping/guest-sessions/revoke' \
  --header "Authorization: Bearer $GUEST_ACCESS_TOKEN"
```

API возвращает `204 No Content`. Повторный отзыв того же известного токена, в том числе истёкшего, также возвращает `204`. Отсутствующий или неизвестный токен возвращает `401 INVALID_GUEST_TOKEN`.

После отзыва ресурс остаётся защищённым: запросы без токена не разрешаются. Отзыв гостевого токена не отзывает ранее созданные публичные ссылки — для них предусмотрен отдельный [метод отзыва](dialog_sharing.md#post-dialogssharerevoke).

## Ошибки и ограничения

| HTTP | Причина | Что делать клиенту |
| --- | --- | --- |
| `400` | `Invalid request body` — неверные поля регистрации; `Unknown tenant` — неизвестная секция. | Проверить ID секции и тело запроса. |
| `401` | `GUEST_TOKEN_REQUIRED` — запрос к гостевому ресурсу отправлен без гостевого токена. | Проверить, что сохранённый токен добавлен в заголовок. Не сбрасывать сессию из-за пропущенного заголовка. |
| `401` | `INVALID_GUEST_TOKEN` — токен некорректен, неизвестен, истёк или отозван. | Проверить переданный токен. Если он недействителен, показать ошибку сессии; повторная выдача токена этим методом не поддерживается. |
| `403` | `GUEST_RESOURCE_MISMATCH` — токен относится к другой секции или ресурсу; `IP_NOT_ALLOWED` — IP не разрешён для секции. | Проверить идентификаторы и условия подключения. Не создавать новую сессию автоматически. |
| `404` | Диалог отсутствует или не принадлежит указанному ресурсу. | Проверить параметры соответствующего метода. |
| `409` | `RESOURCE_ALREADY_EXISTS` — ресурс уже зарегистрирован или имеет историю. | Использовать сохранённый токен, если он есть. Повторная выдача токена или перевод существующего ресурса этим методом не поддерживаются; не подменять ID пользователя. |
| `429` | Превышен лимит регистрации. | Подождать число секунд из заголовка `Retry-After`. |
| `503` | `AUTH_STORAGE_UNAVAILABLE` — сервис не смог проверить или сохранить сессию. | Сохранить текущую сессию и предложить повторить позже. |

Регистрация ограничена 20 попытками в минуту с одного IP на экземпляр сервиса. CORS разрешает заголовок `Authorization`; запросы `OPTIONS` не требуют токена. Ответы используют `Cache-Control: no-store`.

Ошибки `403`, `404` и `503` сами по себе не требуют замены токена. После сетевой ошибки не повторяйте генерацию автоматически: результат мог уже сохраниться. Для повтора того же сообщения соблюдайте [правила повторных запросов](generate.md#ошибки-и-повтор-запроса).
