---
title: "telephony.incoming / web.incoming"
description: "Блокувальний вебхук, який у реальному часі налаштовує конфігурацію вхідного дзвінка."
---

Коли вхідний телефонний виклик надходить на номер **без призначеного
агента** або сеанс вебвіджета запускається з публічним ключем у
`mode="webhook"`, ThunderPhone надсилає **блокувальний**
запит `telephony.incoming` / `web.incoming` на вашу
[застарілу URL-адресу вебхука](/api-reference/organizations#legacy-single-url-webhook)
і очікує відповідь із конфігурацією: до **13 секунд** для
телефонного виклику, включно з однією повторною спробою, і до **10 секунд** для сеансу
віджета (див. [Тайм-аути та повторні спроби](#timeouts-and-retries)). Використовуйте цей
обмін, щоб динамічно вибирати промпт, голос і інструменти для кожного виклику —
повний сценарій див. в [посібнику з динамічної конфігурації викликів](/uk/guides/dynamic-call-config).

<Note>
  Підписані [кінцеві точки вебхуків](/uk/webhooks/endpoints) також отримують
  `telephony.incoming` / `web.incoming` — для **кожного** вхідного дзвінка
  та вебсеансу, незалежно від конфігурації агента, — але ці доставки є
  сповіщеннями без очікування відповіді з `event_id`, а не блокувальними.
  Лише застарілий вебхук з однією URL-адресою передає обмін конфігурацією,
  описаний на цій сторінці. Форми сповіщень кінцевих точок наведено в
  [каталозі подій](/uk/webhooks/events).
</Note>

## Тайм-аути та повторні спроби

Відповідайте швидко: абонент чекає на лінії, поки ви ухвалюєте рішення.

Для телефонного дзвінка (`telephony.incoming`):

- Перша спроба може тривати до **10 секунд**.
- Якщо вона перевищує час очікування, не може встановити з’єднання або отримує відповідь
  `5xx`, `408` чи `429`, ThunderPhone робить ще одну спробу. Повторна спроба отримує весь час, що
  залишився від бюджету в **13 секунд** для всього обміну: приблизно 3 секунди після
  першої спроби, що перевищила час очікування, і майже всі 13 секунд після швидкого збою.
  Повторна спроба не виконується, якщо залишилося б менше ніж 2 секунди.
- Будь-яка інша відповідь `4xx`, відповідь, що перевищує
  [обмеження розміру](#response-size-limit), або відповідь `2xx`, тіло якої не є
  дійсною конфігурацією, є остаточною та не повторюється.
- Обидві спроби надсилають однакове тіло, той самий заголовок `X-ThunderPhone-Signature`
  і той самий заголовок `X-ThunderPhone-Idempotency-Key`
  (`telephony-incoming-<call_id>`), тому обробник із побічними ефектами
  може розпізнати повторну спробу.

Резервного агента немає. Якщо придатна для використання конфігурація не надходить,
дзвінок завершується одразу, а не залишає абонента на беззвучній
лінії. В історії дзвінків він відображається як невдалий дзвінок без тарифікації з
`end_reason` `inbound_config_webhook_failed`, і надсилається звичайна
подія [`telephony.complete`](/uk/webhooks/call-complete).

Сеанс віджета (`web.incoming`) має одну спробу тривалістю до 10
секунд. Якщо вона не вдається, запит сеансу завершується з `502` або `422`.

<Warning>
  **Дзвінки, налаштовані через вебхук, не містять повідомлення ThunderPhone
  про згоду.** Дзвінки, налаштовані через цей обмін, оминають повідомлення
  агента на початку дзвінка та прямо виключені з механізму ThunderPhone щодо
  повідомлень про згоду (Умови надання послуг, розділ «Записування та згода»).
  Ваша організація несе повну відповідальність за всі повідомлення про запис,
  моніторинг, участь ШІ та ідентифікацію абонента, а також за всю необхідну
  згоду на такі дзвінки — дзвінки все одно можуть записуватися,
  транскрибуватися, аналізуватися та обслуговуватися ШІ. Додайте необхідні
  розкриття інформації до власного сценарію дзвінка, перш ніж увімкнути цей
  шлях.
</Warning>

## Тіло запиту

Для телефонних дзвінків (`telephony.incoming`):

```json
{
  "type": "telephony.incoming",
  "data": {
    "call_id":     987654321,
    "from_number": "+14155550199",
    "to_number":   "+15551234567"
  }
}
```

| Поле | Тип | Опис |
|-------|------|-------------|
| `call_id` | integer | Ідентифікатор дзвінка — сталий для всіх подій цього дзвінка |
| `from_number` | string | Номер абонента у форматі E.164 |
| `to_number` | string | Призначення у форматі E.164 (один із ваших номерів ThunderPhone) |

Для сеансів вебвіджета (`web.incoming`) об’єкт `data` ідентифікує
сторінку, де вбудовано віджет, замість телефонних номерів:

```json
{
  "type": "web.incoming",
  "data": {
    "call_id": 987654322,
    "origin_domain": "https://example.com",
    "publishable_key_prefix": "pk_live_a1b2"
  }
}
```

| Поле | Тип | Опис |
|-------|------|-------------|
| `call_id` | ціле число | Ідентифікатор виклику |
| `origin_domain` | рядок | Джерело сторінки, на якій розміщено віджет, як його надіслав браузер. Небраузерний клієнт може надіслати будь-яке значення, тому не вважайте його доказом походження запиту |
| `publishable_key_prefix` | рядок | Перші символи публічного ключа, який відкрив сеанс |
| `language`, `primary_language` | рядок | Наявне, коли сеанс віджета запитав перевизначення мови |
| `voice` | рядок | Наявне, коли сеанс віджета запитав перевизначення голосу |
| `website_context` | рядок | Наявне, коли віджет передав контекст сторінки для конкретного сеансу |

Поля перевизначення (`language`, `voice`, `website_context` і будь-які
браузерні `variables`) надходять лише тоді, коли публічний ключ дозволяє їх у
своїх [перевизначеннях браузера](/uk/guides/embed-web-widget). Запит сеансу, що містить перевизначення, не дозволене ключем, відхиляється до виклику цього вебхука.

<Note>
  Віджети в режимі вебхука надсилають цей запит до власного `webhook_url`
  публічного ключа, якщо його встановлено, інакше — до URL-адреси вебхука
  на рівні організації. В обох випадках запит підписується `secret` вебхука
  організації.
</Note>

---

## Схема відповіді

Поверніть JSON-об’єкт, що описує конфігурацію агента для цього виклику.
`prompt` і `voice` є обов’язковими; усе інше — необов’язкове.

```json
{
  "prompt":  "You are a helpful booking assistant for Acme Restaurant.",
  "voice":   "john",
  "product": "spark",
  "background_track": null,
  "tools":   []
}
```

| Поле | Тип | Обов’язково | Опис |
|-------|------|----------|-------------|
| `prompt` | рядок | так | Системний промпт, який керує агентом |
| `voice` | рядок | так | Ідентифікатор голосу з [`GET /v1/voices`](/api-reference/agents#voices), наприклад `john`. `voice_name` приймається як псевдонім. Невідомі голоси не проходять валідацію та відхиляють виклик |
| `product` | рядок | ні | За замовчуванням — `spark`. Дозволено: `spark`, `bolt`, `storm-base`, `storm-base-with-ack`, `storm-extra`, `storm-extra-with-ack` |
| `thinking_level` | рядок | ні | `minimal`, `base` (за замовчуванням) або `extra`. Для продуктів Storm значення перевизначається: `storm-extra*` примусово встановлює `extra`, інші `storm-*` — `base` |
| `audio_context_mode` | рядок | ні | `full` (за замовчуванням) або `reduced` |
| `watchdog_enabled` | булеве значення | ні | Увімкнути моніторинг для цього виклику. За замовчуванням — `false` |
| `additional_audio_context` | булеве значення \| null | ні | Додавати кілька останніх реплік аудіо абонента, а не лише найостаннішу репліку, що покращує виправлення та збір даних із великою кількістю написань і чисел за незначного збільшення затримки й вартості. За замовчуванням увімкнено для вхідних сеансів і вимкнено для вихідних телефонних викликів; `null` зберігає значення за замовчуванням |
| `storm_feedback_mode` | рядок | ні | `none`, `acknowledgement` (за замовчуванням) або `tick` |
| `language` | рядок | ні | Скорочення для `primary_language` |
| `primary_language` | рядок | ні | Нормалізований код мови (за замовчуванням `en`). Коди, які неможливо розпізнати, відхиляють виклик |
| `has_additional_languages` | булеве значення | ні | За замовчуванням — `false` |
| `additional_languages` | масив рядків | ні | Додаткові мови, на які агент може перемикатися |
| `native_voice_switching` | булеве значення | ні | За замовчуванням — `false`. Коли виклик перемикається на іншу мову, замінює голос на голос, носій якого розмовляє цією мовою, із відповідністю за статтю, замість збереження налаштованого голосу |
| `language_switching` | рядок \| null | ні | `ask_first` (за замовчуванням) або `follow_caller`; `null` має те саме значення, що й `ask_first`. Визначає, як багатомовний агент реагує, коли співрозмовник говорить іншою з його мов. `ask_first` одразу перемикається, коли співрозмовник просить цю мову або говорить нею повне речення, і запитує, чи продовжувати цією мовою, після короткого висловлювання, наприклад «sí». `follow_caller` перемикається на будь-яку налаштовану мову, якою говорить співрозмовник, навіть після одного слова. Застосовується лише коли задано `additional_languages` |
| `background_track` | рядок \| null | ні | Ідентифікатор фонового аудіо або `null` |
| `acknowledgement_prompt_mode` | рядок | ні | `auto` (за замовчуванням) або `manual` (для продуктів Storm-with-ack) |
| `acknowledgement_prompt` | рядок | ні | Використовується, коли `acknowledgement_prompt_mode="manual"` |
| `silence_interval_seconds` | ціле число \| null | ні | 5–120. Секунди тиші абонента перед перевіркою зв’язку |
| `silence_max_checkins` | ціле число \| null | ні | 1–10 |
| `silence_checkins_enabled` | булеве значення | ні | За замовчуванням — `true` |
| `connect_tone_enabled` | булеве значення | ні | За замовчуванням — `false` |
| `pronunciation_dicts` | булеве значення \| null | ні | За замовчуванням — `false`; `null` має те саме значення, що й `false`. Коли встановлено `true`, синтез мовлення Cartesia для виклику використовує словник вимови ThunderPhone для мови виклику, якщо такий існує. Працює для телефонних викликів і викликів у віджеті. Застосовується лише до власних голосів Cartesia від ThunderPhone, а не до голосів із вашим власним ключем API Cartesia. Не використовується з `agent_id`, для якого застосовуються налаштування збереженого агента |
| `voicemail_action` | рядок | ні | `prompt` (за замовчуванням), `hangup` або `message` |
| `voicemail_message` | рядок | ні | Використовується, коли `voicemail_action="message"` |
| `agent_name` | рядок | ні | Відображуване ім’я, яке передається на панелі керування та у віджет |
| `org_name` | рядок | ні | Відображувана назва організації для персони агента |
| `tools` | масив | ні | Вбудовані схеми інструментів-функцій (див. [Інструменти-функції](/uk/tools/overview)) |
| `transfer_destinations` | масив об’єктів \| null | ні | До 50 записів `{name, number}`, на які цей виклик може бути переадресовано; вони перевіряються так само, як [номери переадресації](/api-reference/agents#transfer-numbers) агента. Агент переадресовує за ім’ям і лише на ці номери; `[]` означає відсутність переадресацій. Не вказуйте це поле або надішліть `null`, щоб дозволити агенту переадресувати виклик на вибраний ним номер |
| `timezone` | рядок | ні | Часовий пояс IANA для цього виклику, наприклад `America/Chicago`. Агент називає дату й час у ньому замість використання власного часового поясу. Також дозволено поруч із `agent_id`. Значення, які не є часовими поясами IANA, ігноруються. Див. [Установлення часового поясу для кожного виклику](/uk/guides/dynamic-call-config) |
| `call_id` | ціле число | ні | Необов’язкове повторення ідентифікатора виклику із запиту; ігнорується |

<Note>
  Невідомі ключі верхнього рівня непомітно **ігноруються** — поле з
  помилкою в назві не відхиляє конфігурацію, але не застосовується. Порядок
  мовлення та `max_hold_seconds` тут не приймаються; їх можна
  налаштувати лише в самому [агенті](/api-reference/agents).
</Note>

Оскільки `prompt` і `voice` є обов’язковими, повернення `{}` або будь-якої
відповіді, що не проходить валідацію, відхиляє виклик: телефонний виклик
завершується, як описано в [Тайм-аути та повторні спроби](#timeouts-and-retries), а запит
на сесію віджета завершується помилкою `422`. Для цього шляху немає резервного
статичного агента (номер або ключ у режимі вебхука не має призначеного
агента).

---

## Обмеження розміру відповіді

<Warning>
  Відповіді конфігурації обмежені **5 MiB**. Якщо обробник
  повертає більшу відповідь, зокрема зі статусом `2xx`,
  ThunderPhone повідомляє, що відповідь перевищила обмеження, і
  відхиляє виклик або сеанс віджета. Залишайте у відповіді лише поля,
  потрібні для налаштування виклику; розміщуйте великі дані за
  інструментами функцій або іншим сервісом, а не вбудовуйте їх у конфігурацію.
</Warning>

---

## Приклад обробника

<CodeGroup>
```python Python (FastAPI)
import hashlib
import hmac
import json
import os

from fastapi import FastAPI, HTTPException, Request

app = FastAPI()
WEBHOOK_SECRET = os.environ["THUNDERPHONE_WEBHOOK_SECRET"]

def verify(body: bytes, signature: str) -> bool:
    expected = hmac.new(WEBHOOK_SECRET.encode(), body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, signature or "")

@app.post("/thunderphone-webhook")
async def webhook(request: Request):
    body = await request.body()
    if not verify(body, request.headers.get("X-ThunderPhone-Signature", "")):
        raise HTTPException(status_code=401)

    event = json.loads(body)
    if event["type"] == "telephony.incoming":
        caller = event["data"]["from_number"]
        prompt = (
            "Greet the caller as a San Francisco local…"
            if caller.startswith("+1415")
            else "You are a friendly customer support agent…"
        )
        return {
            "prompt": prompt,
            "voice": "john",
            "product": "spark",
        }
    if event["type"] == "web.incoming":
        return {
            "prompt": "You are the website's helpful voice assistant…",
            "voice": "john",
            "product": "spark",
        }
    return {}
```

```javascript Node.js (Express)
import crypto from "node:crypto";
import express from "express";

const app = express();
const SECRET = process.env.THUNDERPHONE_WEBHOOK_SECRET;

function verify(body, signature) {
  const expected = crypto
    .createHmac("sha256", SECRET)
    .update(body)
    .digest("hex");
  return signature &&
    crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signature));
}

app.post(
  "/thunderphone-webhook",
  express.raw({ type: "application/json" }),
  (req, res) => {
    if (!verify(req.body, req.header("X-ThunderPhone-Signature"))) {
      return res.sendStatus(401);
    }
    const event = JSON.parse(req.body.toString("utf8"));

    if (event.type === "telephony.incoming" || event.type === "web.incoming") {
      const caller = event.data.from_number || "web";
      const prompt = caller.startsWith("+1415")
        ? "Greet the caller as a San Francisco local…"
        : "You are a friendly customer support agent…";
      return res.json({
        prompt,
        voice: "john",
        product: "spark",
      });
    }
    res.json({});
  },
);
```
</CodeGroup>

---

## Відповідь з інструментами функцій

Додайте інструменти, щоб ШІ міг викликати ваші API під час розмови:

```json
{
  "prompt":  "You are a booking assistant. Use the available tools to help customers schedule appointments.",
  "voice":   "john",
  "product": "spark",
  "tools": [
    {
      "type": "function",
      "function": {
        "name": "search_appointments",
        "description": "Find available appointment slots",
        "parameters": {
          "type": "object",
          "properties": {
            "date": { "type": "string", "description": "YYYY-MM-DD" },
            "service": { "type": "string" }
          },
          "required": ["date"]
        }
      },
      "endpoint": {
        "url": "https://api.example.com/appointments/search",
        "method": "POST",
        "headers": {
          "X-Api-Key": "your-key"
        }
      }
    }
  ]
}
```

<Tip>
  Запити до кінцевих точок інструментів підписуються **тим самим
  секретом вебхука організації**, яким було підписано цей обмін. Дивіться
  [Інструменти функцій](/uk/tools/overview), щоб дізнатися точну структуру та формат
  підписаного запиту.
</Tip>

---

## Коротка довідка щодо рівнів продукту

| Продукт | Затримка | Міркування | Підтвердження |
|---------|---------|-----------|-----------------|
| `spark` | Найнижча | Базові | — |
| `bolt` | Низька | Покращені | — |
| `storm-base` | Середня | Потужні | — |
| `storm-base-with-ack` | Середня | Потужні | Автоматична заповнювальна фраза під час обдумування |
| `storm-extra` | Вища | Глибокі | — |
| `storm-extra-with-ack` | Вища | Глибокі | Автоматична заповнювальна фраза під час обдумування |

---

## Пов’язане

<CardGroup cols={2}>
  <Card title="telephony.complete / web.complete" icon="phone-slash" href="/uk/webhooks/call-complete">
    Неблокувальна подія завершення виклику.
  </Card>
  <Card title="Інструменти функцій" icon="screwdriver-wrench" href="/uk/tools/overview">
    Повна JSON-схема для `tools[]` і контракт підписаної кінцевої точки.
  </Card>
  <Card title="Кінцеві точки вебхуків" icon="bolt" href="/uk/webhooks/endpoints">
    Підпишіть кілька URL-адрес на `telephony.incoming` / `web.incoming`.
  </Card>
  <Card title="Динамічна конфігурація виклику" icon="wand-magic-sparkles" href="/uk/guides/dynamic-call-config">
    Шаблони для промптів, інструментів і A/B-тестів для кожного абонента.
  </Card>
</CardGroup>
