> ## 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.

# Przegląd webhooków

> Jak ThunderPhone dostarcza zdarzenia w czasie rzeczywistym, jak weryfikować podpisy oraz jak wypada porównanie starszego modelu dostarczania z modelem opartym na punktach końcowych.

ThunderPhone wysyła żądania HTTP `POST` do Twojego serwera, gdy podczas połączenia
coś się wydarzy — rozpocznie się połączenie przychodzące, połączenie się zakończy, zakończy się
uruchomienie oceny, zostanie wyzwolony alert itd. Dostępne są **dwa modele
dostarczania**:

<CardGroup cols={2}>
  <Card title="Endpointy webhooków (zalecane)" icon="bolt" href="/pl/webhooks/endpoints">
    Wiele adresów URL, sekrety dla poszczególnych endpointów, filtry zdarzeń dla poszczególnych endpointów
    oraz automatyczne ponawianie prób.
    Zarządzaj przez `GET/POST/PATCH/DELETE /v1/developer/webhook-endpoints`.
  </Card>

  <Card title="Starszy webhook z pojedynczym adresem URL" icon="link" href="/api-reference/organizations#legacy-single-url-webhook">
    Jeden adres URL na organizację. Obejmuje zdarzenia cyklu życia połączenia, w tym
    **blokujące** wymiany konfiguracji. Zarządzany przez `GET/PUT /v1/webhook`.
  </Card>
</CardGroup>

Wszystkie dziesięć typów zdarzeń z [katalogu zdarzeń](/pl/webhooks/events) jest
dostarczanych przez endpointy webhooków. Sześć zdarzeń cyklu życia połączenia
(`telephony.incoming`, `telephony.complete`, `telephony.tool`,
`web.incoming`, `web.complete`, `web.tool`) jest **również** wysyłanych do
starszego webhooka z pojedynczym adresem URL — jeśli masz zarówno starszy adres URL, jak i
pasujący endpoint, otrzymasz zdarzenie na **obu** ścieżkach. Zachowanie blokujące
([wymiana konfiguracji `telephony.incoming` / `web.incoming`](/pl/webhooks/call-incoming) oraz
[wywoływanie narzędzi](/pl/tools/overview) w trybie webhooka)
występuje wyłącznie na starszej ścieżce; każde dostarczenie do endpointu jest
powiadomieniem typu fire-and-forget.

## Format ładunku

Dostarczenia do endpointów mają postać obiektu JSON z polami `data`, `event_id` i
`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` jest unikalny dla każdego wyemitowanego zdarzenia. Jest identyczny przy ponownych próbach
**oraz** we wszystkich endpointach, które otrzymują zdarzenie — używaj go do deduplikacji.

Starszy webhook z pojedynczym adresem URL wysyła te same pola `type` i `data`, ale
**bez** `event_id`:

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

W transmisji każde ciało jest serializowane kanonicznie — klucze są sortowane
alfabetycznie, bez białych znaków, w UTF-8. Przykłady z formatowaniem w tej
dokumentacji służą wyłącznie czytelności.

Pełną listę typów zdarzeń i pól ładunku znajdziesz w [katalogu zdarzeń](/pl/webhooks/events).

## Weryfikacja podpisu

Każde żądanie zawiera podpis HMAC-SHA256 obejmujący **surową treść
żądania** w nagłówku `X-ThunderPhone-Signature`. Kluczem podpisującym jest
`secret` punktu końcowego (lub `secret` webhooka na poziomie organizacji w przypadku
starszych dostaw).

### Kroki

1. Odczytaj surową treść żądania **przed** jakimkolwiek parsowaniem.
2. Oblicz `hmac_sha256(secret, body).hexdigest()`.
3. Porównaj w czasie stałym z nagłówkiem `X-ThunderPhone-Signature`.

Podpisujemy dokładnie bajty, które przesyłamy, a są one
kanoniczną serializacją JSON (posortowane klucze, kompaktowe separatory). Dlatego
weryfikacja względem surowej treści zawsze działa — a jeśli framework
udostępnia tylko sparsowany JSON, ponowna serializacja z posortowanymi kluczami i
kompaktowymi separatorami daje identyczne bajty. Oba podejścia opisano w
[przewodniku po weryfikacji](/pl/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>

## Semantyka dostarczania

Ta semantyka dotyczy dostarczania do **punktów końcowych**. Starszy webhook z pojedynczym adresem URL wykonuje jedną synchroniczną próbę bez ponowień.

<AccordionGroup>
  <Accordion title="Ponowienia">
    Każde zdarzenie jest natychmiast podejmowane raz. Każda odpowiedź `2xx`
    potwierdza dostarczenie. W przypadku każdego innego wyniku (innego niż 2xx,
    błędu połączenia, przekroczenia czasu) ponawiamy próbę **po 1 min, 5 min, 30 min, 2 godz.,
    6 godz., 12 godz. i 24 godz. od pierwszej próby** — łącznie 8 prób w ciągu
    24 godzin. Jeśli wszystkie próby się nie powiodą, dostarczanie zostaje zatrzymane, a punkt końcowy
    otrzymuje oznaczenie `status="failing"` w
    [punktach końcowych webhooków](/pl/webhooks/endpoints). Zwróć `2xx`, gdy tylko
    ładunek zostanie trwale zaakceptowany; przetwarzaj go asynchronicznie.
  </Accordion>

  <Accordion title="Kolejność">
    Kolejność dostarczania jest zapewniana w miarę możliwości. W praktyce dostarczamy zdarzenia w
    kolejności ich emisji, ale ponowienia mogą zmienić kolejność po niepowodzeniu.
    Zawsze usuwaj duplikaty i uzgadniaj dane według `call_id` / identyfikatora obiektu.
  </Accordion>

  <Accordion title="Duplikaty">
    Dostarczanie jest **co najmniej jednokrotne**: ponowienie po odpowiedzi, której nie
    otrzymaliśmy, może zduplikować zdarzenie. Każde ponowienie zawiera ten sam
    `event_id`, dlatego przechowuj identyfikatory przetworzonych zdarzeń i pomijaj powtórzenia. `event_id` jest
    również współdzielony między punktami końcowymi — dwa punkty końcowe subskrybujące
    to samo zdarzenie otrzymują ten sam `event_id`.
  </Accordion>

  <Accordion title="Limity czasu">
    Dostarczanie do punktów końcowych ma limit czasu **30 s** na próbę. W starszej ścieżce
    blokujące żądania sterujące zachowaniem aktywnego połączenia —
    wymiana konfiguracji [`telephony.incoming` / `web.incoming`](/pl/webhooks/call-incoming) —
    przekraczają limit czasu po **10 s**, ale wolna odpowiedź opóźnia odebranie połączenia,
    dlatego staraj się odpowiedzieć w ciągu kilku sekund. Dyspozycja narzędzi w trybie webhooków
    [tool dispatch](/pl/tools/overview) pozwala na 20 s.
  </Accordion>

  <Accordion title="Źródłowe adresy IP">
    Wychodzące webhooki pochodzą z zakresu adresów IP chmury ThunderPhone.
    Jeśli zapora sieciowa wymaga listy dozwolonych adresów, skontaktuj się z pomocą techniczną, a
    udostępnimy aktualne zakresy.
  </Accordion>
</AccordionGroup>

## Wybór między starszymi webhookami a webhookami opartymi na punktach końcowych

| Funkcja                        | Starsze (`/v1/webhook`)                                                   | Punkty końcowe (`/v1/developer/webhook-endpoints`) |
| ------------------------------ | ------------------------------------------------------------------------- | -------------------------------------------------- |
| Liczba adresów URL             | 1 na organizację                                                          | Wiele na organizację                               |
| Zakres zdarzeń                 | Tylko `telephony.*` / `web.*`                                             | Wszystkie 10 typów zdarzeń                         |
| Filtr zdarzeń                  | —                                                                         | Dla każdego punktu końcowego                       |
| Ponowienia                     | Brak                                                                      | 8 prób w ciągu 24 godz.                            |
| Koperta                        | `type` + `data`                                                           | `type` + `data` + `event_id`                       |
| Rotacja sekretu                | Zastępuje pojedynczy sekret                                               | Sekret dla każdego punktu końcowego                |
| Wyłączenie bez usuwania        | —                                                                         | `status=disabled`                                  |
| Widoczność stanu               | —                                                                         | `active` / `disabled` / `failing`                  |
| Blokująca wymiana konfiguracji | Tak ([`telephony.incoming` / `web.incoming`](/pl/webhooks/call-incoming)) | Nigdy — tylko powiadomienia                        |
| Najlepsze zastosowanie         | Dynamiczna konfiguracja połączeń                                          | Obsługa zdarzeń na produkcji                       |

Nowe integracje powinny odbierać zdarzenia przez webhooki oparte na
punktach końcowych. Zachowaj (lub dodaj) starszy adres URL tylko wtedy, gdy konfigurujesz połączenia
dynamicznie w momencie odbierania albo używasz dyspozycji narzędzi w trybie webhooków — te
wymiany żądań i odpowiedzi działają wyłącznie w starszej ścieżce.

***

## Powiązane

<CardGroup cols={2}>
  <Card title="Katalog zdarzeń" icon="list" href="/pl/webhooks/events">
    Wszystkie typy zdarzeń i ich ładunki.
  </Card>

  <Card title="Punkty końcowe webhooków" icon="bolt" href="/pl/webhooks/endpoints">
    Zarządzaj wieloma punktami końcowymi, filtrami zdarzeń i sekretami.
  </Card>

  <Card title="telephony.incoming / web.incoming" icon="phone" href="/pl/webhooks/call-incoming">
    Blokujące żądanie, na które serwer musi odpowiedzieć, aby skonfigurować połączenia.
  </Card>

  <Card title="telephony.complete / web.complete" icon="phone" href="/pl/webhooks/call-complete">
    Ładunek po połączeniu z transkrypcją, nagraniem i metrykami.
  </Card>
</CardGroup>
