> ## Documentation Index
> Fetch the complete documentation index at: https://docs.thunderphone.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Огляд вебхуків

> Як 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 theme={null}
{
  "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 theme={null}
{
  "type": "telephony.incoming",
  "data": { "call_id": 987654321, "from_number": "+14155550199", "to_number": "+15551234567" }
}
```

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

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

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

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

### Кроки

1. Прочитайте необроблене тіло запиту **до** будь-якого аналізу.
2. Обчисліть `hmac_sha256(secret, body).hexdigest()`.
3. Порівняйте в сталий час із заголовком `X-ThunderPhone-Signature`.

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

<CodeGroup>
  ```python Python theme={null}
  import hmac
  import hashlib

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

  # Example Flask handler
  from flask import Flask, request, abort
  app = Flask(__name__)

  @app.post("/thunderphone-webhook")
  def handle():
      body = request.get_data()
      sig = request.headers.get("X-ThunderPhone-Signature", "")
      if not verify_signature(body, sig, WEBHOOK_SECRET):
          abort(401)
      event = request.get_json()
      # dispatch on event["type"] …
      return "", 204
  ```

  ```javascript Node.js (Express) theme={null}
  import crypto from "node:crypto";
  import express from "express";

  function verifySignature(body, signature, secret) {
    const expected = crypto
      .createHmac("sha256", secret)
      .update(body)
      .digest("hex");
    if (!signature || expected.length !== signature.length) return false;
    return crypto.timingSafeEqual(
      Buffer.from(expected),
      Buffer.from(signature),
    );
  }

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

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

Ця семантика застосовується до доставок **кінцевих точок**. Застарілий вебхук з однією URL-адресою виконує одну синхронну спробу без повторних спроб.

<AccordionGroup>
  <Accordion title="Повторні спроби">
    Кожна подія одразу надсилається один раз. Будь-яка відповідь `2xx`
    підтверджує доставку. За будь-якого іншого результату (не-2xx,
    помилка з’єднання, тайм-аут) ми повторюємо спробу через **1 хв, 5 хв, 30 хв, 2 год, 6 год,
    12 год і 24 год після першої спроби** — 8 спроб протягом
    24 годин. Якщо всі спроби завершаться невдачею, доставка припиняється, а кінцева точка
    позначається як `status="failing"` у
    [кінцевих точках вебхуків](/uk/webhooks/endpoints). Поверніть `2xx`, щойно
    дані буде надійно прийнято; обробляйте їх асинхронно.
  </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 с**, але повільна відповідь затримує прийняття дзвінка,
    тому намагайтеся відповідати протягом кількох секунд. Надсилання інструментів у режимі вебхуків
    [tool dispatch](/uk/tools/overview) допускає 20 с.
  </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`                      |
| Ротація секрету                  | Замінює єдиний секрет                                                     | Секрет для кожної кінцевої точки                  |
| Вимкнення без видалення          | —                                                                         | `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>
