---
title: "Встройте веб-виджет"
description: "Добавьте голосового агента на свой маркетинговый сайт или сайт поддержки с помощью одного тега скрипта — посетители смогут общаться с ним прямо в браузере, без номера телефона."
---

Веб-виджет позволяет посетителям вашего сайта начать разговор с ИИ-агентом по нажатию кнопки, используя микрофон браузера. Это отдельный JavaScript / React SDK с собственной [справочной документацией SDK](/ru/widget/overview) — в этом руководстве описана настройка на стороне ThunderPhone, необходимая для виджета.

<Note>
  Всё это можно сделать без cURL: страница панели **Веб-виджеты**
  (`/dashboard/web-widgets`) создаёт виджет, задаёт его режим
  и агента, управляет разрешёнными доменами и предоставляет сниппет для встраивания.
</Note>

## Предварительные требования

<Steps>
  <Step title="Создайте агента">
    Агент, чьи инструкции и голос будут использоваться в сеансе виджета. Установите
    `widget_enabled: true` (значение по умолчанию).
  </Step>
  <Step title="Выберите режим маршрутизации">
    - `mode="agent"` — один статический агент для каждого ключа. Самый простой вариант.
    - `mode="webhook"` — ваш сервер выбирает агента для каждого посетителя через
      [вебхук `web.incoming`](/ru/webhooks/call-incoming). Используйте этот вариант для
      авторизованных пользователей, A/B-тестов или маршрутизации по страницам.
  </Step>
  <Step title="Укажите разрешённые домены">
    Укажите каждое имя хоста, на котором будет встроен виджет. Браузеры отправляют
    источник страницы с каждым запросом сеанса, поэтому список не позволит вашему ключу
    работать, если кто-то скопирует его на другой сайт. Это не аутентификация: скрипт вне
    браузера может отправить любой заголовок `Origin`. `localhost` не разрешён автоматически в продакшене; чтобы
    тестировать локально с API продакшена, добавьте `localhost` в список.
  </Step>
  <Step title="Выберите переопределения браузера">
    Определите, какие настройки сеанса может отправлять ваша страница. Новые ключи не принимают
    ни одной; см. [Переопределения браузера](#browser-overrides).
  </Step>
</Steps>

## Создайте публикуемый ключ

<CodeGroup>
```bash Static agent
curl -X POST https://api.thunderphone.com/v1/publishable-key \
  -H "Authorization: Bearer sk_live_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name":            "Marketing site (prod)",
    "mode":            "agent",
    "agent_id":        12,
    "allowed_domains": ["example.com", "*.example.com"],
    "allowed_overrides": ["context"]
  }'
```

```bash Dynamic via webhook
curl -X POST https://api.thunderphone.com/v1/publishable-key \
  -H "Authorization: Bearer sk_live_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name":            "Support (dynamic)",
    "mode":            "webhook",
    "webhook_url":     "https://example.com/thunderphone/widget-hook",
    "allowed_domains": ["support.example.com"]
  }'
```
</CodeGroup>

Ответ содержит `key`, начинающийся с `pk_live_...`. **Публикуемые
ключи по своей конструкции являются публичными** — их можно безопасно включать в сборку фронтенда.
Полный список полей см. в [справочнике публикуемых ключей](/api-reference/publishable-keys).

<Warning>
  `allowed_domains` должен содержать хотя бы одну запись. `*.example.com`
  соответствует поддоменам (например, `api.example.com`), но **не** основному
  домену. Простые подстановочные знаки, такие как `*` или `*.*`, отклоняются.
</Warning>

## Добавьте виджет на сайт

В [документации SDK виджета](/ru/widget/overview) описаны три варианта
интеграции:

<CardGroup cols={3}>
  <Card title="React-компонент" icon="react" href="/ru/widget/react">
    `<ThunderPhoneWidget publishableKey="pk_live_..." />`.
  </Card>
  <Card title="Хук без интерфейса" icon="circle-nodes" href="/ru/widget/headless-hook">
    `useThunderPhone()` для пользовательских интерфейсов.
  </Card>
  <Card title="Тег скрипта CDN" icon="code" href="/ru/widget/cdn-script-tag">
    `ThunderPhone.mount({...})` для сайтов без сборщика.
  </Card>
</CardGroup>

Все три варианта принимают один и тот же `publishableKey` и отображают кнопку микрофона
вместе с аудиоэлементом во время вызова.

## Переопределения в браузере

Виджет может отправлять со страницы четыре настройки для каждого сеанса:

| Настройка | Свойство виджета | Что изменяет |
|---------|-------------|-----------------|
| `context` | `context` | Текст со страницы, добавляемый в промпт агента в качестве справочной информации |
| `variables` | нет (только необработанный `POST /v1/widget/session`) | Значения для заполнителей `{{variable}}` в промпте |
| `voice` | `voice` | Голос агента для этого сеанса |
| `language` | `language` | Язык, с которого начинается сеанс |

Ваш публикуемый ключ является общедоступным, поэтому любой, у кого он есть, может отправлять эти
настройки, в том числе из скрипта, который имитирует ваш домен. Поэтому каждая из них
отключена, пока вы не разрешите её для ключа: установите флажок в разделе **Переопределения браузера**
при создании или редактировании виджета в панели управления либо задайте
`allowed_overrides` через
[API публикуемых ключей](/api-reference/publishable-keys). Разрешайте только те настройки,
которые действительно отправляет ваша страница.

Запрос сеанса, содержащий настройку, которую ключ не разрешает, завершается
ошибкой `400` и указывает её:

```json
{
  "error": "override_not_allowed",
  "fields": ["voice"],
  "message": "This publishable key does not allow the browser to set `voice`. ..."
}
```

Проверка применяется в обоих режимах. В режиме вебхука разрешённая настройка
также пересылается в ваш вебхук `web.incoming` (`context` поступает как
`website_context`). Ключи, созданные до появления этой настройки, разрешают все
четыре, поэтому существующие встраивания продолжают работать; отключите то, чем не пользуетесь.

Значение `context` виджета обрезается до 12 000 символов (примерно 3 400
токенов типичного английского текста) и учитывается в
[доплате за размер промпта](/ru/guides/billing-and-topups).
Агенту указано воспринимать его как содержимое страницы, а не как инструкции.

## Вебхуки режима виджета

Когда `mode="webhook"`, ThunderPhone вызывает ваш `webhook_url` при каждом
запуске сеанса с полезной нагрузкой `web.incoming`. Верните конфигурацию агента,
которую нужно запустить для этого посетителя — она использует ту же
[схему ответа](/ru/webhooks/call-incoming), что и телефонные
звонки:

```json
{
  "prompt":  "You are a VIP concierge for Jane Doe.",
  "voice":   "john",
  "product": "storm-base",
  "tools":   [ /* per-customer tools */ ]
}
```

Вы можете добавлять в промпт контекст из собственного сеанса (какой клиент просматривает
сайт, на какой он странице) и менять агентов для каждого этапа развёртывания.

## Отслеживание сессий

Сессии виджета отображаются в
[`GET /v1/calls`](/api-reference/calls#list-calls) с
`direction="widget"` — с той же расшифровкой, записью, оценкой и
тарификацией, что и телефонные звонки. Фильтруйте по `direction`, чтобы создать панель управления только для виджета.

---

## Следующие шаги

<CardGroup cols={2}>
  <Card title="Справочник SDK виджета" icon="window-maximize" href="/ru/widget/overview">
    Подробности интеграции React / hook / CDN.
  </Card>
  <Card title="Динамическая конфигурация для каждого звонка" icon="bolt" href="/ru/guides/dynamic-call-config">
    Реализуйте поток `mode="webhook"` от начала до конца.
  </Card>
  <Card title="Справочник публикуемых ключей" icon="key" href="/api-reference/publishable-keys">
    Все поля ресурса ключа.
  </Card>
  <Card title="API сессий микрофона" icon="microphone" href="/api-reference/mic-sessions">
    Пропустите виджет; управляйте LiveKit напрямую для пользовательских интерфейсов.
  </Card>
</CardGroup>
