---
title: "Přehled webhooků"
description: "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 události — začne příchozí hovor, hovor skončí, dokončí se
vyhodnocení, aktivuje se upozornění a podobně. Existují **dva modely
doručování**:

<CardGroup cols={2}>
  <Card title="Koncové body webhooků (doporučeno)" icon="bolt" href="/cs/webhooks/endpoints">
    Více URL, tajné klíče pro jednotlivé koncové body, filtry událostí pro jednotlivé koncové body
    a automatické opakování pokusů.
    Spravujte pomocí `GET/POST/PATCH/DELETE /v1/developer/webhook-endpoints`.
  </Card>
  <Card title="Starší webhook s jednou URL" icon="link" href="/api-reference/organizations#legacy-single-url-webhook">
    Jedna URL pro každou organizaci. Obsahuje události životního cyklu hovoru včetně
    **blokujících** výměn konfigurace. Spravuje se pomocí `GET/PUT /v1/webhook`.
  </Card>
</CardGroup>

Všechny uvedené typy událostí v [katalogu událostí](/cs/webhooks/events) se
doručují prostřednictvím koncových bodů webhooků. Šest událostí životního cyklu hovoru
(`telephony.incoming`, `telephony.complete`, `telephony.tool`,
`web.incoming`, `web.complete`, `web.tool`) se **také** odesílá do
staršího webhooku s jednou URL — pokud máte starší URL i odpovídající
koncový bod, obdržíte událost 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 dostupné výhradně na starší cestě; každé doručení do koncového bodu je
oznámení typu fire-and-forget.

## Formát datové části

Doručení do koncového bodu jsou objektem JSON s `data`, `event_id` a
`type`:

```json
{
  "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 odeslanou událost. Je totožné při opakovaných pokusech
**i** ve všech koncových bodech, které událost obdrží — deduplikujte podle něj.

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

```json
{
  "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 mezer, v kódování 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 naleznete v
[katalogu událostí](/cs/webhooks/events).

## Ověření podpisu

Každý požadavek obsahuje dva podpisy, oba HMAC-SHA256 s klíčem `secret`
koncového bodu (nebo `secret` webhooku na úrovni vaší organizace pro starší
doručování):

- `X-ThunderPhone-Signature-V2: t=<unix seconds>,v1=<hex>` podepisuje
  časové razítko, metodu, cestu a dotaz a nezpracované tělo. Ověřte
  tento podpis a odmítněte požadavky, jejichž `t` se liší od vašich hodin o více než **5 minut**,
  aby zachycený požadavek nemohl být později znovu přehrán. Při opakovaném odeslání jsou pokusy
  podepsány znovu.
- `X-ThunderPhone-Signature` podepisuje pouze nezpracované tělo. Stále se odesílá
  beze změny, aby stávající ověřovače nadále fungovaly.

### Kroky

1. Před jakýmkoli parsováním načtěte hlavičku, metodu, nezpracovanou cestu a dotaz a nezpracované tělo.
2. Pomocí svého tajného klíče vypočítejte hexadecimální HMAC-SHA256 z
   `<t>\n<METHOD>\n<path and query>\n<body>`.
3. Požadavek odmítněte, pokud se `t` liší o více než 300 sekund nebo pokud se výsledek při porovnání v konstantním čase neshoduje s `v1`.

Těla jsou kanonickou serializací JSON (seřazené klíče, kompaktní oddělovače) a podepisujeme přesně ty bajty, které přenášíme. [Průvodce ověřením](/cs/guides/verify-webhook-signatures) obsahuje přesný podepisovaný řetězec, praktický příklad, kód bez SDK a starší hlavičku.

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

## Sémantika doručování

Tyto sémantiky se vztahují na doručování do **endpointů**. Starší webhook s jednou adresou URL provede jeden synchronní pokus bez opakování, s výjimkou výměny konfigurace telefonního hovoru
[`telephony.incoming`](/cs/webhooks/call-incoming),
která se opakuje jednou.

<AccordionGroup>
  <Accordion title="Opakování">
    Každá událost se ihned zkusí doručit jednou. Jakákoli odpověď `2xx`
    potvrzuje doručení. Při jakémkoli jiném výsledku (jiné než 2xx,
    chyba připojení, časový limit) doručení zopakujeme **1 m, 5 m, 30 m, 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
    se v [endpointech webhooků](/cs/webhooks/endpoints) označí jako
    `status="failing"`; vlastníkům a administrátorům vaší organizace
    odešleme e-mail a zobrazíme to v dashboardu. Vraťte `2xx`, jakmile je
    datová část trvale přijata; zpracujte ji asynchronně.
    Testovací odeslání z testovacího API endpointu dostanou jedno opakování přibližně minutu po
    prvním pokusu a nikdy nezmění stav endpointu.
  </Accordion>

  <Accordion title="Pořadí">
    Pořadí doručování není zaručeno. V praxi doručujeme události v
    pořadí, v jakém jsou emitovány, ale opakování může při selhání pořadí změnit.
    Vždy odstraňujte duplicity a slaďujte data podle `call_id` / ID objektu.
  </Accordion>

  <Accordion title="Duplicity">
    Doručování je **alespoň jednou**: opakování po odpovědi, kterou jsme
    neobdrželi, může vytvořit duplicitní událost. Každé opakování nese stejné
    `event_id`, proto ukládejte ID zpracovaných událostí a opakování přeskakujte. `event_id` je
    také sdíleno mezi koncovými body — dva koncové body přihlášené ke
    stejné události obdrží stejné `event_id`.
  </Accordion>

  <Accordion title="Časové limity">
    Doručení do endpointů mají časový limit **30 s** na pokus. Ve
    starší cestě jsou 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) —
    omezeny na **10 s** pro první pokus telefonního hovoru a na **13 s** pro celou výměnu včetně
    jednoho opakování a na **10 s** pro relaci widgetu. Pomalá odpověď zpozdí
    přijetí hovoru, proto se snažte odpovědět během několika sekund. [Spouštění nástrojů](/cs/tools/overview) v režimu webhooku
    ve výchozím nastavení umožňuje 20 s a deklarace nástrojů mohou nastavit `timeout` nejvyšší úrovně.
  </Accordion>

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

## Volba mezi staršími webhooky a webhooky založenými na koncových bodech

| Funkce | Starší (`/v1/webhook`) | Koncové body (`/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ý koncový bod |
| Opakování | Žádné, kromě jednoho při výměně konfigurace telefonního hovoru | 8 pokusů během 24 h |
| Obálka | `type` + `data` | `type` + `data` + `event_id` |
| Rotace tajného klíče | Nahrazuje jeden tajný klíč | Tajný klíč pro každý koncový bod |
| Zakázání bez odstranění | `PUT /v1/webhook` s `{"url": ""}` | `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
koncových bodech. Starší adresu URL ponechte (nebo přidejte) pouze v případě, že hovory
konfigurujete dynamicky při jejich přijetí nebo používáte odesílání nástrojů v režimu webhooků — 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 datové obsahy.
  </Card>
  <Card title="Koncové body webhooků" icon="bolt" href="/cs/webhooks/endpoints">
    Spravujte více koncových bodů, 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">
    Datový obsah po hovoru s přepisem, nahrávkou a metrikami.
  </Card>
</CardGroup>
