---
title: "Огляд вебхуків"
description: "Як ThunderPhone надсилає події в реальному часі, як перевіряти підписи та як порівнюються застаріла й кінцева моделі доставки."
---

ThunderPhone надсилає HTTP-запити `POST` на ваш сервер, коли під час дзвінка
відбуваються події — починається вхідний дзвінок, завершується дзвінок, завершується
перевірка оцінювання, спрацьовує сповіщення тощо. Є **дві моделі
доставки**:

<CardGroup cols={2}>
  <Card title="Кінцеві точки вебхуків (рекомендовано)" icon="bolt" href="/uk/webhooks/endpoints">
    Кілька URL-адрес, секрети для кожної кінцевої точки, фільтри подій для кожної кінцевої точки
    та автоматичні повторні спроби.
    Керуйте через `GET/POST/PATCH/DELETE /v1/developer/webhook-endpoints`.
  </Card>
  <Card title="Застарілий вебхук з однією URL-адресою" icon="link" href="/api-reference/organizations#legacy-single-url-webhook">
    Одна URL-адреса на організацію. Передає події життєвого циклу дзвінка, зокрема
    **блокувальні** обміни конфігурацією. Керується через `GET/PUT /v1/webhook`.
  </Card>
</CardGroup>

Усі перелічені типи подій у [каталозі подій](/uk/webhooks/events)
доставляються через кінцеві точки вебхуків. Шість подій життєвого циклу дзвінка
(`telephony.incoming`, `telephony.complete`, `telephony.tool`,
`web.incoming`, `web.complete`, `web.tool`) **також** надсилаються до
застарілого вебхука з однією URL-адресою — якщо у вас є і застаріла URL-адреса, і
відповідна кінцева точка, ви отримуєте подію за **обома** шляхами. Блокувальна
поведінка ([обмін конфігурацією `telephony.incoming` / `web.incoming`](/uk/webhooks/call-incoming)
та [виклик інструментів](/uk/tools/overview) у режимі вебхуків)
доступна виключно через застарілий шлях; кожна доставка до кінцевої точки є
сповіщенням без очікування відповіді.

## Формат корисного навантаження

Доставки до кінцевих точок містять JSON-об’єкт із `data`, `event_id` та
`type`:

```json
{
  "data": {
    "call_id": 987654321,
    "from_number": "+14155550199",
    "to_number": "+15551234567"
  },
  "event_id": "3f6b2ad0-1c9e-4a57-9f2b-8f6f0f9d2f11",
  "type": "telephony.incoming"
}
```

`event_id` є унікальним для кожної надісланої події. Він однаковий для повторних спроб
**і** для кожної кінцевої точки, що отримує подію — виконуйте дедуплікацію за ним.

Застарілий вебхук з однією URL-адресою надсилає ті самі `type` і `data`, але
**без** `event_id`:

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

Під час передавання кожне тіло серіалізується канонічно — ключі відсортовано
за алфавітом, без пробілів, UTF-8. Відформатовані приклади в цій
документації наведено лише для зручності читання.

Перегляньте [каталог подій](/uk/webhooks/events), щоб отримати повний список типів подій
і полів корисного навантаження.

## Перевірка підпису

Кожен запит містить два підписи, обидва HMAC-SHA256 з ключем `secret`
кінцевої точки (або `secret` вебхука на рівні вашої організації для застарілих
доставок):

- `X-ThunderPhone-Signature-V2: t=<unix seconds>,v1=<hex>` підписує
  часову позначку, метод, шлях і параметри запиту, а також необроблене тіло. Перевіряйте
  цей підпис і відхиляйте запити, у яких `t` відрізняється від
  вашого часу більш ніж на **5 хвилин**, щоб перехоплений запит не можна було відтворити пізніше. Повторні спроби
  підписуються повторно під час надсилання.
- `X-ThunderPhone-Signature` підписує лише необроблене тіло. Його й надалі надсилають
  без змін, щоб наявні засоби перевірки продовжували працювати.

### Кроки

1. Зчитайте заголовок, метод, необроблений шлях і параметри запиту, а також необроблене
   тіло **до** будь-якого аналізу.
2. Обчисліть шістнадцяткове значення HMAC-SHA256 для
   `<t>\n<METHOD>\n<path and query>\n<body>` за допомогою вашого секрету.
3. Відхиліть запит, якщо `t` відхиляється більш ніж на 300 секунд або якщо
   результат не збігається з `v1` під час порівняння за сталий час.

Тіла запитів є канонічною серіалізацією JSON (відсортовані ключі, компактні
роздільники), і ми підписуємо саме ті байти, які передаємо. [Посібник із
перевірки](/uk/guides/verify-webhook-signatures) містить точний підписуваний
рядок, приклад із розрахунком, код без SDK і застарілий заголовок.

<CodeGroup>
```python Python (Flask)
from flask import Flask, abort, request
from thunderphone import verify_signature

app = Flask(__name__)

@app.post("/thunderphone-webhook")
def handle():
    query = request.query_string.decode()
    if not verify_signature(
        request.headers.get("X-ThunderPhone-Signature-V2"),
        secret=WEBHOOK_SECRET,
        method=request.method,
        path=request.path + (f"?{query}" if query else ""),
        body=request.get_data(),  # raw bytes
    ):
        abort(401)
    event = request.get_json()
    # dispatch on event["type"] …
    return "", 204
```

```javascript Node.js (Express)
import express from "express";
import { verifySignature } from "@thunderphone/sdk";

const app = express();
app.post(
  "/thunderphone-webhook",
  express.raw({ type: "application/json" }),
  (req, res) => {
    const ok = verifySignature({
      header: req.header("X-ThunderPhone-Signature-V2"),
      secret: process.env.WEBHOOK_SECRET,
      method: req.method,
      path: req.originalUrl,
      body: req.body,
    });
    if (!ok) return res.sendStatus(401);
    const event = JSON.parse(req.body.toString("utf8"));
    // dispatch on event.type …
    res.sendStatus(204);
  },
);
```
</CodeGroup>

## Семантика доставки

Ця семантика застосовується до доставок **кінцевих точок**. Застарілий
вебхук з однією URL-адресою виконує одну синхронну спробу без повторних спроб, за винятком
обміну конфігурацією телефонного дзвінка
[`telephony.incoming`](/uk/webhooks/call-incoming),
який повторюється один раз.

<AccordionGroup>
  <Accordion title="Повторні спроби">
    Для кожної події негайно виконується одна спроба. Будь-яка відповідь `2xx`
    підтверджує доставку. За будь-якого іншого результату (не-2xx,
    помилка підключення, тайм-аут) ми повторюємо спробу через **1 хв, 5 хв, 30 хв, 2 год, 6 год,
    12 год і 24 год після першої спроби** — 8 спроб протягом
    24 годин. Якщо всі спроби завершуються невдачею, доставка припиняється, а кінцева точка
    позначається як `status="failing"` у
    [кінцевих точках вебхуків](/uk/webhooks/endpoints); ми надсилаємо електронний лист
    власникам і адміністраторам вашої організації та показуємо це на панелі керування. Поверніть `2xx`, щойно
    корисне навантаження буде надійно прийнято; обробляйте його асинхронно.
    Тестові надсилання з test API кінцевої точки отримують одну повторну спробу приблизно через хвилину після
    першої спроби та ніколи не змінюють статус кінцевої точки.
  </Accordion>

  <Accordion title="Порядок">
    Порядок доставки забезпечується настільки, наскільки це можливо. На практиці ми доставляємо події
    у порядку їх створення, але повторні спроби можуть змінити порядок у разі помилки.
    Завжди виконуйте дедуплікацію та звірку за `call_id` / ідентифікатором об’єкта.
  </Accordion>

  <Accordion title="Дублікати">
    Доставка виконується **щонайменше один раз**: повторна спроба після відповіді, якої ми не
    отримали, може дублювати подію. Кожна повторна спроба містить той самий
    `event_id`, тому зберігайте оброблені ідентифікатори та пропускайте повтори. `event_id` також
    спільний для всіх ендпойнтів — два ендпойнти, підписані на
    ту саму подію, отримують той самий `event_id`.
  </Accordion>

  <Accordion title="Тайм-аути">
    Доставки до кінцевих точок мають тайм-аут **30 с** для кожної спроби. У
    застарілому шляху блокувальні запити, що керують поведінкою активного дзвінка, —
    обмін конфігурацією
    [`telephony.incoming` / `web.incoming`](/uk/webhooks/call-incoming) —
    обмежені **10 с** для першої спроби телефонного дзвінка та **13 с** для всього обміну, включно з однією
    повторною спробою, а також **10 с** для сеансу віджета. Повільна відповідь затримує
    прийняття дзвінка, тому намагайтеся відповідати протягом кількох секунд. [Диспетчеризація інструментів](/uk/tools/overview) у режимі вебхука
    за замовчуванням допускає 20 с, а оголошення інструментів можуть задавати `timeout`
    на верхньому рівні.
  </Accordion>

  <Accordion title="Вихідні IP-адреси">
    Вихідні вебхуки надходять із діапазону хмарних IP-адрес ThunderPhone.
    Якщо ваш брандмауер потребує списку дозволених адрес, зверніться до служби підтримки, і ми
    надамо актуальні діапазони.
  </Accordion>
</AccordionGroup>

## Вибір між застарілими вебхуками та вебхуками на основі ендпойнтів

| Функція | Застарілий (`/v1/webhook`) | Кінцеві точки (`/v1/developer/webhook-endpoints`) |
|---------|------------------------|----------------------------------------------|
| Кількість URL-адрес | 1 на організацію | Багато на організацію |
| Охоплення подій | Лише `telephony.*` / `web.*` | Усі 10 типів подій |
| Фільтр подій | — | Для кожної кінцевої точки |
| Повторні спроби | Немає, окрім однієї для обміну конфігурацією телефонного виклику | 8 спроб протягом 24 год |
| Конверт | `type` + `data` | `type` + `data` + `event_id` |
| Ротація секрету | Замінює один секрет | Секрет для кожної кінцевої точки |
| Вимкнення без видалення | `PUT /v1/webhook` з `{"url": ""}` | `status=disabled` |
| Відображення статусу | — | `active` / `disabled` / `failing` |
| Блокувальний обмін конфігурацією | Так ([`telephony.incoming` / `web.incoming`](/uk/webhooks/call-incoming)) | Ніколи — лише сповіщення |
| Найкраще підходить для | Динамічної конфігурації викликів | Обробки подій у продакшні |

Нові інтеграції мають отримувати події через вебхуки на основі
ендпойнтів. Зберігайте (або додавайте) застарілу URL-адресу лише якщо ви динамічно налаштовуєте дзвінки
під час прийняття або використовуєте виклик інструментів у режимі вебхуків — ці
обміни запитами/відповідями працюють лише через застарілий шлях.

---

## Пов’язані матеріали

<CardGroup cols={2}>
  <Card title="Каталог подій" icon="list" href="/uk/webhooks/events">
    Усі типи подій і їхні корисні навантаження.
  </Card>
  <Card title="Кінцеві точки вебхуків" icon="bolt" href="/uk/webhooks/endpoints">
    Керуйте кількома кінцевими точками, фільтрами подій і секретами.
  </Card>
  <Card title="telephony.incoming / web.incoming" icon="phone" href="/uk/webhooks/call-incoming">
    Блокувальний запит, на який ваш сервер має відповісти, щоб налаштувати дзвінки.
  </Card>
  <Card title="telephony.complete / web.complete" icon="phone" href="/uk/webhooks/call-complete">
    Корисне навантаження після дзвінка з транскриптом, записом і метриками.
  </Card>
</CardGroup>
