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

# Přehled webhooků

> Jak ThunderPhone doručuje události v reálném čase, jak ověřovat podpisy a jak se porovnávají starší model doručování a model založený na koncových bodech.

ThunderPhone odesílá na váš server požadavky HTTP `POST`, když během hovoru
nastanou určité události — začne příchozí hovor, hovor skončí, dokončí se
spuštění hodnocení, aktivuje se upozornění a podobně. Existují **dva modely
doručování**:

<CardGroup cols={2}>
  <Card title="Webhookové endpointy (doporučeno)" icon="bolt" href="/cs/webhooks/endpoints">
    Více URL, tajné klíče pro jednotlivé endpointy, filtry událostí pro jednotlivé endpointy
    a automatické opakování pokusů.
    Správa prostřednictvím `GET/POST/PATCH/DELETE /v1/developer/webhook-endpoints`.
  </Card>

  <Card title="Starší webhook s jedinou URL" icon="link" href="/api-reference/organizations#legacy-single-url-webhook">
    Jedna URL pro každou organizaci. Obsahuje události životního cyklu hovorů včetně
    **blokujících** výměn konfigurace. Správa prostřednictvím `GET/PUT /v1/webhook`.
  </Card>
</CardGroup>

Všech deset typů událostí v [katalogu událostí](/cs/webhooks/events) je
doručováno prostřednictvím webhookových endpointů. Šest událostí životního cyklu hovorů
(`telephony.incoming`, `telephony.complete`, `telephony.tool`,
`web.incoming`, `web.complete`, `web.tool`) se **také** odesílá do
staršího webhooku s jedinou URL — pokud máte starší URL i odpovídající endpoint,
událost obdržíte na **obou** cestách. Blokující chování ( [výměna konfigurace
`telephony.incoming` / `web.incoming`](/cs/webhooks/call-incoming) a odesílání
[nástrojů](/cs/tools/overview) v režimu webhooku) je
výhradně na starší cestě; každé doručení do endpointu je oznámení bez čekání na odpověď.

## Formát datové části

Doručení do endpointu jsou objektem JSON s `data`, `event_id` a
`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` je jedinečné pro každou emitovanou událost. Je stejné při opakovaných pokusech
**i** pro každý endpoint, který událost přijme — deduplikujte podle něj.

Starší webhook s jedinou URL odesílá stejné `type` a `data`, ale
**bez** `event_id`:

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

Při přenosu je každá datová část serializována kanonicky — klíče jsou řazeny
abecedně, bez bílých znaků, v UTF-8. Příklady s formátováním v této
dokumentaci slouží pouze pro lepší čitelnost.

Úplný seznam typů událostí a polí datové části najdete v [katalogu událostí](/cs/webhooks/events).

## Ověření podpisu

Každý požadavek obsahuje podpis HMAC-SHA256 nad **nezpracovaným tělem
požadavku** v hlavičce `X-ThunderPhone-Signature`. Podepisovací klíč je
`secret` koncového bodu (nebo `secret` webhooku na úrovni vaší organizace
pro starší doručení).

### Postup

1. Přečtěte nezpracované tělo požadavku **před** jakýmkoli parsováním.
2. Vypočítejte `hmac_sha256(secret, body).hexdigest()`.
3. Porovnejte jej v konstantním čase s hlavičkou `X-ThunderPhone-Signature`.

Podepisujeme přesně bajty, které odesíláme, a tyto bajty představují
kanonickou serializaci JSON (seřazené klíče, kompaktní oddělovače). Ověření
oproti nezpracovanému tělu proto vždy funguje — a pokud vám váš framework
předává pouze parsovaný JSON, opětovná serializace se seřazenými klíči a
kompaktními oddělovači vytvoří totožné bajty. Oba postupy jsou popsány v
[návodu k ověření](/cs/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>

## Sémantika doručování

Tato sémantika platí pro doručování do **endpointů**. Starší webhook s jedinou URL
provádí jeden synchronní pokus bez opakování.

<AccordionGroup>
  <Accordion title="Opakování">
    Každá událost je ihned odeslána jednou. Jakákoli odpověď `2xx`
    potvrzuje doručení. Při jakémkoli jiném výsledku (jiném než 2xx,
    chybě připojení, vypršení časového limitu) pokus zopakujeme za **1 min, 5 min, 30 min, 2 h, 6 h,
    12 h a 24 h po prvním pokusu** — celkem 8 pokusů během
    24 hodin. Pokud selžou všechny pokusy, doručování se zastaví a endpoint
    je v [endpointech webhooků](/cs/webhooks/endpoints) označen stavem
    `status="failing"`. Vraťte `2xx`, jakmile je
    payload trvale přijat; zpracujte jej asynchronně.
  </Accordion>

  <Accordion title="Řazení">
    Pořadí doručování je zajišťováno s nejlepším možným úsilím. V praxi doručujeme události v
    pořadí, v jakém jsou emitovány, opakované pokusy je však při selhání mohou přeřadit.
    Vždy deduplikujte a slaďte podle `call_id` / ID objektu.
  </Accordion>

  <Accordion title="Duplicity">
    Doručování probíhá **alespoň jednou**: opakovaný pokus po odpovědi, kterou jsme
    neobdrželi, může událost zdvojit. Každý opakovaný pokus obsahuje stejné
    `event_id`, proto ukládejte ID zpracovaných událostí a opakování vynechávejte. `event_id` je
    také sdíleno mezi endpointy — dva endpointy odebírající stejnou událost obdrží stejné `event_id`.
  </Accordion>

  <Accordion title="Časové limity">
    Doručování do endpointů má pro každý pokus časový limit **30 s**. Ve
    starší cestě blokující požadavky, které řídí chování probíhajícího hovoru —
    výměna konfigurace [`telephony.incoming` / `web.incoming`](/cs/webhooks/call-incoming) —
    vyprší po **10 s**, pomalá odpověď však zpozdí přijetí hovoru, proto
    se snažte odpovědět během několika sekund. Odesílání nástrojů v režimu webhooku [tool dispatch](/cs/tools/overview) umožňuje 20 s.
  </Accordion>

  <Accordion title="Zdrojové IP adresy">
    Odchozí webhooky pocházejí z cloudového rozsahu IP adres ThunderPhone.
    Pokud váš firewall vyžaduje seznam povolených adres, kontaktujte podporu a
    sdělíme vám aktuální rozsahy.
  </Accordion>
</AccordionGroup>

## Volba mezi staršími webhooky a webhooky založenými na endpointech

| Funkce                       | Starší (`/v1/webhook`)                                                    | Endpointy (`/v1/developer/webhook-endpoints`) |
| ---------------------------- | ------------------------------------------------------------------------- | --------------------------------------------- |
| Počet URL                    | 1 na organizaci                                                           | Více na organizaci                            |
| Pokrytí událostí             | Pouze `telephony.*` / `web.*`                                             | Všech 10 typů událostí                        |
| Filtr událostí               | —                                                                         | Pro každý endpoint                            |
| Opakování                    | Žádné                                                                     | 8 pokusů během 24 h                           |
| Obálka                       | `type` + `data`                                                           | `type` + `data` + `event_id`                  |
| Rotace tajného klíče         | Nahradí jediný tajný klíč                                                 | Tajný klíč pro každý endpoint                 |
| Zakázání bez odstranění      | —                                                                         | `status=disabled`                             |
| Viditelnost stavu            | —                                                                         | `active` / `disabled` / `failing`             |
| Blokující výměna konfigurace | Ano ([`telephony.incoming` / `web.incoming`](/cs/webhooks/call-incoming)) | Nikdy — pouze oznámení                        |
| Nejvhodnější pro             | Dynamickou konfiguraci hovorů                                             | Zpracování událostí v produkci                |

Nové integrace by měly zpracovávat události prostřednictvím webhooků založených na
endpointech. Starší URL ponechte (nebo přidejte) pouze tehdy, pokud
dynamicky konfigurujete hovory při přijetí nebo používáte odesílání nástrojů v režimu webhooku — tyto
výměny požadavků a odpovědí fungují pouze ve starší cestě.

***

## Související

<CardGroup cols={2}>
  <Card title="Katalog událostí" icon="list" href="/cs/webhooks/events">
    Všechny typy událostí a jejich payloady.
  </Card>

  <Card title="Endpointy webhooků" icon="bolt" href="/cs/webhooks/endpoints">
    Spravujte více endpointů, filtry událostí a tajné klíče.
  </Card>

  <Card title="telephony.incoming / web.incoming" icon="phone" href="/cs/webhooks/call-incoming">
    Blokující požadavek, na který musí váš server odpovědět, aby nakonfiguroval hovory.
  </Card>

  <Card title="telephony.complete / web.complete" icon="phone" href="/cs/webhooks/call-complete">
    Payload po hovoru s přepisem, nahrávkou a metrikami.
  </Card>
</CardGroup>
