---
title: "Overzicht van webhooks"
description: "Hoe ThunderPhone realtime gebeurtenissen levert, hoe je handtekeningen verifieert en hoe de verouderde en eindpuntgebaseerde leveringsmodellen zich tot elkaar verhouden."
---

ThunderPhone stuurt HTTP-`POST`-verzoeken naar je server wanneer er iets
gebeurt tijdens een oproep — een inkomende oproep begint, een oproep eindigt, een beoordelingsrun
wordt voltooid, een waarschuwing wordt geactiveerd, enzovoort. Er zijn **twee leveringsmodellen**:

<CardGroup cols={2}>
  <Card title="Webhook-eindpunten (aanbevolen)" icon="bolt" href="/nl/webhooks/endpoints">
    Meerdere URL's, geheimen per eindpunt, gebeurtenisfilters per eindpunt
    en automatische nieuwe pogingen.
    Beheer via `GET/POST/PATCH/DELETE /v1/developer/webhook-endpoints`.
  </Card>
  <Card title="Verouderde webhook met één URL" icon="link" href="/api-reference/organizations#legacy-single-url-webhook">
    Eén URL per organisatie. Bevat de gebeurtenissen in de
    oproeplevenscyclus, inclusief de **blokkerende** configuratie-uitwisselingen.
    Beheer via `GET/PUT /v1/webhook`.
  </Card>
</CardGroup>

Alle vermelde gebeurtenistypen in de [gebeurteniscatalogus](/nl/webhooks/events) worden
geleverd via webhook-eindpunten. De zes gebeurtenissen in de oproeplevenscyclus
(`telephony.incoming`, `telephony.complete`, `telephony.tool`,
`web.incoming`, `web.complete`, `web.tool`) worden **ook** naar de
verouderde webhook met één URL gestuurd — als je zowel een verouderde URL als een
overeenkomend eindpunt hebt, ontvang je de gebeurtenis via **beide** paden. Blokkerend
gedrag (de configuratie-uitwisseling voor [`telephony.incoming` / `web.incoming`](/nl/webhooks/call-incoming)
en [tool-dispatch](/nl/tools/overview) in webhookmodus)
bestaat uitsluitend op het verouderde pad; elke levering aan een eindpunt is een
fire-and-forgetmelding.

## Payloadindeling

Leveringen aan eindpunten zijn een JSON-object met `data`, `event_id` en
`type`:

```json
{
  "data": {
    "call_id": 987654321,
    "from_number": "+14155550199",
    "to_number": "+15551234567"
  },
  "event_id": "3f6b2ad0-1c9e-4a57-9f2b-8f6f0f9d2f11",
  "type": "telephony.incoming"
}
```

`event_id` is uniek voor elke verzonden gebeurtenis. Het is identiek bij nieuwe pogingen
**en** bij elk eindpunt dat de gebeurtenis ontvangt — dedupliceer op basis hiervan.

De verouderde webhook met één URL stuurt hetzelfde `type` en dezelfde `data`, maar
**zonder** `event_id`:

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

Over het netwerk wordt elke body canoniek geserialiseerd — sleutels alfabetisch gesorteerd,
geen witruimte, UTF-8. De mooi opgemaakte voorbeelden in
deze documentatie zijn uitsluitend bedoeld voor leesbaarheid.

Zie de [gebeurteniscatalogus](/nl/webhooks/events) voor de volledige lijst met gebeurtenistypen
en payloadvelden.

## Handtekeningverificatie

Elke aanvraag bevat twee handtekeningen, beide HMAC-SHA256 met als sleutel de
`secret` van het eindpunt (of je webhook-`secret` op organisatieniveau voor verouderde
leveringen):

- `X-ThunderPhone-Signature-V2: t=<unix seconds>,v1=<hex>` ondertekent de
  tijdstempel, de methode, het pad en de query, en de onbewerkte body. Verifieer
  deze en weiger aanvragen waarvan `t` meer dan **5 minuten** afwijkt van
  je klok, zodat een onderschepte aanvraag later niet opnieuw kan worden afgespeeld.
  Nieuwe pogingen worden opnieuw ondertekend wanneer ze worden verzonden.
- `X-ThunderPhone-Signature` ondertekent alleen de onbewerkte body. Deze wordt
  nog steeds ongewijzigd verzonden, zodat bestaande verificateurs blijven werken.

### Stappen

1. Lees de header, de methode, het onbewerkte pad en de query, en de onbewerkte
   body **vóór** enige parsing.
2. Bereken de hexadecimale HMAC-SHA256 van
   `<t>\n<METHOD>\n<path and query>\n<body>` met je geheim.
3. Weiger de aanvraag als `t` meer dan 300 seconden afwijkt, of als het
   resultaat niet overeenkomt met `v1` bij een vergelijking in constante tijd.

Bodies zijn de canonieke JSON-serialisatie (gesorteerde sleutels, compacte
scheidingstekens), en we ondertekenen exact de bytes die we verzenden. De
[verificatiehandleiding](/nl/guides/verify-webhook-signatures) bevat de exacte
ondertekende tekenreeks, een uitgewerkt voorbeeld, code zonder de SDK en de
verouderde header.

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

## Bezorgsemantiek

Deze semantiek geldt voor leveringen aan **endpoints**. De verouderde
webhook met één URL voert één synchrone poging uit zonder herpogingen, behalve de
configuratie-uitwisseling voor telefoongesprekken van
[`telephony.incoming`](/nl/webhooks/call-incoming),
waarvoor één herpoging wordt uitgevoerd.

<AccordionGroup>
  <Accordion title="Herpogingen">
    Elk event wordt onmiddellijk één keer geprobeerd. Elke `2xx`-respons
    bevestigt de levering. Bij elke andere uitkomst (geen 2xx,
    verbindingsfout, time-out) proberen we het opnieuw na **1 m, 5 m, 30 m, 2 h, 6 h,
    12 h en 24 h na de eerste poging** — 8 pogingen verspreid over
    24 uur. Als elke poging mislukt, stopt de levering en wordt het endpoint
    gemarkeerd met `status="failing"` in
    [webhook-endpoints](/nl/webhooks/endpoints); we e-mailen de
    eigenaren en beheerders van je organisatie en tonen dit in het dashboard. Retourneer `2xx` zodra
    de payload duurzaam is geaccepteerd; verwerk deze asynchroon.
    Testverzendingen vanuit de endpoint-test-API krijgen ongeveer een minuut na
    de eerste poging één herpoging en wijzigen de status van het endpoint nooit.
  </Accordion>

  <Accordion title="Volgorde">
    De bezorgvolgorde is naar beste vermogen. In de praktijk leveren we in de
    volgorde waarin gebeurtenissen worden verzonden, maar nieuwe pogingen kunnen bij fouten
    de volgorde wijzigen.
    Dedupliqueer en stem altijd af op basis van `call_id` / object-id.
  </Accordion>

  <Accordion title="Duplicaten">
    Bezorging is **minstens één keer**: een nieuwe poging na een reactie die we nooit
    hebben gezien, kan een gebeurtenis dupliceren. Elke nieuwe poging bevat dezelfde
    `event_id`, dus sla verwerkte id's op en sla herhalingen over. `event_id` wordt
    ook gedeeld tussen eindpunten — twee eindpunten die op dezelfde
    gebeurtenis zijn geabonneerd ontvangen dezelfde `event_id`.
  </Accordion>

  <Accordion title="Time-outs">
    Leveringen aan endpoints hebben een time-out van **30 s** per poging. Op het
    verouderde pad zijn blokkerende aanvragen die het gedrag van live oproepen aansturen — de
    configuratie-uitwisseling van
    [`telephony.incoming` / `web.incoming`](/nl/webhooks/call-incoming)
    — beperkt tot **10 s** voor de eerste poging van een telefoongesprek en
    **13 s** voor de volledige uitwisseling, inclusief de ene
    herpoging, en tot **10 s** voor een widgetsessie. Een trage respons vertraagt
    het opnemen van een oproep, dus probeer binnen enkele seconden te antwoorden. Toondispatch in webhookmodus [tooldispatch](/nl/tools/overview) staat standaard 20 s
    toe, en tooldeclaraties kunnen een `timeout` op hoofdniveau instellen.
  </Accordion>

  <Accordion title="Bron-IP's">
    Uitgaande webhooks zijn afkomstig uit het cloud-IP-bereik van ThunderPhone.
    Als je firewall een toelatingslijst vereist, neem dan contact op met support; we
    delen de actuele bereiken.
  </Accordion>
</AccordionGroup>

## Kiezen tussen verouderde en op eindpunten gebaseerde webhooks

| Functie | Legacy (`/v1/webhook`) | Eindpunten (`/v1/developer/webhook-endpoints`) |
|---------|------------------------|----------------------------------------------|
| Aantal URL's | 1 per organisatie | Meerdere per organisatie |
| Gebeurtenisdekking | Alleen `telephony.*` / `web.*` | Alle 10 gebeurtenistypen |
| Gebeurtenisfilter | — | Per eindpunt |
| Nieuwe pogingen | Geen, behalve één voor de configuratie-uitwisseling voor telefoongesprekken | 8 pogingen binnen 24 uur |
| Envelop | `type` + `data` | `type` + `data` + `event_id` |
| Secretrotatie | Vervangt één secret | Secret per eindpunt |
| Uitschakelen zonder verwijderen | `PUT /v1/webhook` met `{"url": ""}` | `status=disabled` |
| Zichtbaarheid van status | — | `active` / `disabled` / `failing` |
| Blokkerende configuratie-uitwisseling | Ja ([`telephony.incoming` / `web.incoming`](/nl/webhooks/call-incoming)) | Nooit — alleen meldingen |
| Meest geschikt voor | Dynamische oproepconfiguratie | Gebeurtenissen verwerken in productie |

Nieuwe integraties moeten gebeurtenissen verwerken via op eindpunten gebaseerde
webhooks. Behoud (of voeg) alleen een verouderde URL toe als je oproepen
dynamisch configureert bij het aannemen of toolverzending in webhookmodus gebruikt — die
verzoek-/reactie-uitwisselingen worden alleen uitgevoerd via het verouderde pad.

---

## Gerelateerd

<CardGroup cols={2}>
  <Card title="Gebeurteniscatalogus" icon="list" href="/nl/webhooks/events">
    Alle gebeurtenistypen en hun payloads.
  </Card>
  <Card title="Webhookeindpunten" icon="bolt" href="/nl/webhooks/endpoints">
    Beheer meerdere eindpunten, gebeurtenisfilters en geheimen.
  </Card>
  <Card title="telephony.incoming / web.incoming" icon="phone" href="/nl/webhooks/call-incoming">
    Het blokkerende verzoek waarop je server moet antwoorden om oproepen te configureren.
  </Card>
  <Card title="telephony.complete / web.complete" icon="phone" href="/nl/webhooks/call-complete">
    Payload na het gesprek met transcript, opname en statistieken.
  </Card>
</CardGroup>
