---
title: "Prehľad webhookov"
description: "Ako ThunderPhone doručuje udalosti v reálnom čase, ako overovať podpisy a ako sa porovnávajú starší model doručovania a model založený na koncových bodoch."
---

ThunderPhone odosiela na váš server požiadavky HTTP `POST`, keď počas hovoru
nastanú udalosti — začne sa prichádzajúci hovor, hovor sa skončí, dokončí sa
hodnotenie, spustí sa upozornenie a podobne. Existujú **dva modely
doručovania**:

<CardGroup cols={2}>
  <Card title="Koncové body webhookov (odporúčané)" icon="bolt" href="/sk/webhooks/endpoints">
    Viacero adries URL, tajné kľúče pre jednotlivé koncové body, filtre udalostí pre jednotlivé koncové body
    a automatické opakovania.
    Spravujte prostredníctvom `GET/POST/PATCH/DELETE /v1/developer/webhook-endpoints`.
  </Card>
  <Card title="Starší webhook s jednou adresou URL" icon="link" href="/api-reference/organizations#legacy-single-url-webhook">
    Jedna adresa URL pre organizáciu. Obsahuje udalosti životného cyklu hovoru vrátane
    **blokujúcich** výmien konfigurácie. Spravuje sa prostredníctvom `GET/PUT /v1/webhook`.
  </Card>
</CardGroup>

Všetkých desať typov udalostí v [katalógu udalostí](/sk/webhooks/events) sa
doručuje prostredníctvom koncových bodov webhookov. Šesť udalostí životného cyklu hovoru
(`telephony.incoming`, `telephony.complete`, `telephony.tool`,
`web.incoming`, `web.complete`, `web.tool`) sa **zároveň** odosiela do
staršieho webhooku s jednou adresou URL — ak máte staršiu adresu URL aj zodpovedajúci
koncový bod, udalosť dostanete na **oboch** cestách. Blokujúce
správanie ([výmena konfigurácie `telephony.incoming` / `web.incoming`](/sk/webhooks/call-incoming)
a [odoslanie nástroja](/sk/tools/overview) v režime webhooku)
je dostupné výlučne na staršej ceste; každé doručenie na koncový bod je
oznámenie typu fire-and-forget.

## Formát dátovej časti

Doručenia na koncový bod sú objekty JSON s položkami `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ý pre každú odoslanú udalosť. Je identický pri opakovaniach
**aj** na všetkých koncových bodoch, ktoré udalosť prijmú — používajte ho na deduplikáciu.

Starší webhook s jednou adresou URL odosiela rovnaké `type` a `data`, ale
**bez** `event_id`:

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

Pri prenose je každé telo serializované kanonicky — kľúče sú zoradené
abecedne, bez medzier, v kódovaní UTF-8. Príklady s formátovaním v tejto
dokumentácii slúžia len na lepšiu čitateľnosť.

Úplný zoznam typov udalostí a polí dátovej časti nájdete v [katalógu udalostí](/sk/webhooks/events).

## Overenie podpisu

Každá požiadavka obsahuje v hlavičke `X-ThunderPhone-Signature` podpis HMAC-SHA256 vytvorený nad **nespracovaným telom požiadavky**. Podpisovací kľúč je `secret` koncového bodu (alebo `secret` webhooku na úrovni vašej organizácie pre staršie doručenia).

### Kroky

1. Prečítajte nespracované telo požiadavky **pred** akýmkoľvek spracovaním.
2. Vypočítajte `hmac_sha256(secret, body).hexdigest()`.
3. Porovnajte výsledok v konštantnom čase s hlavičkou `X-ThunderPhone-Signature`.

Podpisujeme presne tie bajty, ktoré odosielame, a tieto bajty sú kanonickou serializáciou JSON (zoradené kľúče, kompaktné oddeľovače). Overovanie voči nespracovanému telu preto vždy funguje — a ak vám váš framework poskytne iba spracovaný JSON, jeho opätovná serializácia so zoradenými kľúčmi a kompaktnými oddeľovačmi vytvorí identické bajty. Oba postupy sú uvedené v [návode na overenie](/sk/guides/verify-webhook-signatures).

<CodeGroup>
```python Python
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)
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čovania

Táto sémantika sa vzťahuje na doručovanie do **koncových bodov**. Starší webhook s jednou URL je jeden synchrónny pokus bez opakovaní.

<AccordionGroup>
  <Accordion title="Opakovania">
    Každá udalosť sa okamžite pokúsi doručiť raz. Každá odpoveď `2xx`
    potvrdzuje doručenie. Pri akomkoľvek inom výsledku (nie-2xx,
    chyba pripojenia, časový limit) pokus zopakujeme **1 min, 5 min, 30 min, 2 h, 6 h,
    12 h a 24 h po prvom pokuse** — 8 pokusov počas
    24 hodín. Ak zlyhajú všetky pokusy, doručovanie sa zastaví a koncový bod
    sa v [koncových bodoch webhookov](/sk/webhooks/endpoints) označí stavom
    `status="failing"`. Vráťte `2xx` hneď po trvalom prijatí
    payloadu; spracujte ho asynchrónne.
  </Accordion>

  <Accordion title="Poradie">
    Poradie doručovania je v rámci možností zachované. V praxi doručujeme v
    poradí, v akom sú udalosti emitované, ale opakovania môžu pri zlyhaní poradie zmeniť.
    Vždy odstráňte duplicity a zosúlaďte údaje podľa `call_id` / ID objektu.
  </Accordion>

  <Accordion title="Duplikáty">
    Doručovanie je **aspoň raz**: opakovanie po odpovedi, ktorú sme nikdy
    nezaznamenali, môže vytvoriť duplicitnú udalosť. Každé opakovanie obsahuje rovnaké
    `event_id`, preto ukladajte spracované ID a preskakujte opakovania. `event_id` je
    zdieľané aj medzi koncovými bodmi — dva koncové body prihlásené na odber rovnakej
    udalosti dostanú rovnaké `event_id`.
  </Accordion>

  <Accordion title="Časové limity">
    Doručovanie do koncových bodov má časový limit **30 s** na pokus. V staršej ceste
    blokujúce požiadavky, ktoré riadia správanie aktívneho hovoru —
    výmena konfigurácie [`telephony.incoming` / `web.incoming`](/sk/webhooks/call-incoming) —
    vypršia po **10 s**, ale pomalá odpoveď oneskorí prijatie hovoru, preto sa snažte
    odpovedať do niekoľkých sekúnd. Odosielanie nástrojov v režime webhooku
    [tool dispatch](/sk/tools/overview) predvolene umožňuje 20 s a deklarácie nástrojov
    môžu nastaviť `timeout` na najvyššej úrovni.
  </Accordion>

  <Accordion title="Zdrojové IP adresy">
    Odchádzajúce webhooky pochádzajú z cloudového rozsahu IP adries ThunderPhone.
    Ak váš firewall vyžaduje zoznam povolených adries, kontaktujte podporu a
    poskytneme vám aktuálne rozsahy.
  </Accordion>
</AccordionGroup>

## Výber medzi staršími webhookmi a webhookmi založenými na koncových bodoch

| Funkcia | Staršie (`/v1/webhook`) | Koncové body (`/v1/developer/webhook-endpoints`) |
|---------|------------------------|----------------------------------------------|
| Počet URL | 1 na organizáciu | Viac na organizáciu |
| Pokrytie udalostí | Len `telephony.*` / `web.*` | Všetkých 10 typov udalostí |
| Filter udalostí | — | Pre každý koncový bod |
| Opakovania | Žiadne | 8 pokusov počas 24 h |
| Obálka | `type` + `data` | `type` + `data` + `event_id` |
| Rotácia tajného kľúča | Nahrádza jediný tajný kľúč | Tajný kľúč pre každý koncový bod |
| Zakázanie bez odstránenia | `PUT /v1/webhook` s `{"url": ""}` | `status=disabled` |
| Viditeľnosť stavu | — | `active` / `disabled` / `failing` |
| Blokujúca výmena konfigurácie | Áno ([`telephony.incoming` / `web.incoming`](/sk/webhooks/call-incoming)) | Nikdy — iba oznámenia |
| Najvhodnejšie pre | Dynamickú konfiguráciu hovorov | Spracovanie udalostí v produkcii |

Nové integrácie by mali udalosti spracúvať prostredníctvom webhookov založených na
koncových bodoch. Staršiu URL ponechajte (alebo pridajte) len v prípade, že konfigurujete hovory
dynamicky pri ich prijatí alebo používate odosielanie nástrojov v režime webhooku — tieto
výmeny požiadaviek a odpovedí fungujú iba v staršej ceste.

---

## Súvisiace

<CardGroup cols={2}>
  <Card title="Katalóg udalostí" icon="list" href="/sk/webhooks/events">
    Všetky typy udalostí a ich payloady.
  </Card>
  <Card title="Koncové body webhookov" icon="bolt" href="/sk/webhooks/endpoints">
    Spravujte viacero koncových bodov, filtre udalostí a tajné kľúče.
  </Card>
  <Card title="telephony.incoming / web.incoming" icon="phone" href="/sk/webhooks/call-incoming">
    Blokujúca požiadavka, na ktorú musí váš server odpovedať, aby nakonfiguroval hovory.
  </Card>
  <Card title="telephony.complete / web.complete" icon="phone" href="/sk/webhooks/call-complete">
    Payload po hovore s prepisom, nahrávkou a metrikami.
  </Card>
</CardGroup>
