---
title: "Oversikt over webhooks"
description: "Slik leverer ThunderPhone hendelser i sanntid, slik verifiserer du signaturer, og slik sammenlignes de eldre og endepunktbaserte leveringsmodellene."
---

ThunderPhone sender HTTP-`POST`-forespørsler til serveren din når ting
skjer under en samtale — et innkommende anrop starter, en samtale avsluttes, en vurderingskjøring fullføres, et varsel utløses og så videre. Det finnes **to leveringsmodeller**:

<CardGroup cols={2}>
  <Card title="Webhook-endepunkter (anbefalt)" icon="bolt" href="/nb/webhooks/endpoints">
    Flere URL-er, hemmeligheter per endepunkt, hendelsesfiltre per endepunkt
    og automatiske nye forsøk.
    Administrer via `GET/POST/PATCH/DELETE /v1/developer/webhook-endpoints`.
  </Card>
  <Card title="Eldre webhook med én URL" icon="link" href="/api-reference/organizations#legacy-single-url-webhook">
    Én URL per organisasjon. Inneholder hendelsene i samtalens livssyklus, inkludert de
    **blokkerende** konfigurasjonsutvekslingene. Administreres på `GET/PUT /v1/webhook`.
  </Card>
</CardGroup>

Alle oppførte hendelsestyper i [hendelseskatalogen](/nb/webhooks/events)
leveres gjennom webhook-endepunkter. De seks hendelsene i samtalens livssyklus
(`telephony.incoming`, `telephony.complete`, `telephony.tool`,
`web.incoming`, `web.complete`, `web.tool`) sendes **også** til den
eldre webhooken med én URL — hvis du har både en eldre URL og et
matchende endepunkt, mottar du hendelsen på **begge** banene. Blokkerende
atferd ([`telephony.incoming`- / `web.incoming`-konfigurasjonsutvekslingen](/nb/webhooks/call-incoming) og
[verktøysdistribusjon](/nb/tools/overview) i webhook-modus) finnes
utelukkende på den eldre banen; hver levering til et endepunkt er en
fire-and-forget-varsling.

## Nyttelastformat

Leveringer til endepunkter er et JSON-objekt med `data`, `event_id` og
`type`:

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

`event_id` er unik for hver utsendte hendelse. Den er identisk på tvers av nye forsøk
**og** på tvers av hvert endepunkt som mottar hendelsen — dedupliser på den.

Den eldre webhooken med én URL sender samme `type` og `data`, men
**uten** `event_id`:

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

På nettverket serialiseres hver body kanonisk — nøkler sortert
alfabetisk, ingen mellomrom, UTF-8. Eksemplene med pen formatering i
denne dokumentasjonen er kun for lesbarhet.

Se [hendelseskatalogen](/nb/webhooks/events) for hele listen over hendelses-
typer og nyttelastfelt.

## Signaturverifisering

Hver forespørsel har to signaturer, begge HMAC-SHA256 med endepunktets
`secret` som nøkkel (eller webhookens `secret` på organisasjonsnivå for eldre
leveringer):

- `X-ThunderPhone-Signature-V2: t=<unix seconds>,v1=<hex>` signerer
  tidsstempelet, metoden, banen og spørringen samt råteksten. Verifiser
  denne, og avvis forespørsler der `t` avviker med mer enn **5 minutter** fra
  klokken din, slik at en oppfanget forespørsel ikke kan spilles av på nytt senere.
  Nye forsøk signeres på nytt når de sendes.
- `X-ThunderPhone-Signature` signerer bare råteksten. Den sendes fortsatt
  uendret, slik at eksisterende verifiseringsprogrammer fortsetter å fungere.

### Trinn

1. Les headeren, metoden, den rå stien og spørringen samt den rå
   brødteksten **før** eventuell parsing.
2. Beregn den heksadesimale HMAC-SHA256-verdien av
   `<t>\n<METHOD>\n<path and query>\n<body>` med hemmeligheten din.
3. Avvis forespørselen hvis `t` avviker med mer enn 300 sekunder, eller hvis
   resultatet ikke samsvarer med `v1` i en sammenligning med konstant tid.

Brødtekster er den kanoniske JSON-serialiseringen (sorterte nøkler, kompakte
skilletegn), og vi signerer nøyaktig byteverdiene vi overfører. [Veiledningen for
verifisering](/nb/guides/verify-webhook-signatures) inneholder den nøyaktige
signerte strengen, et gjennomarbeidet eksempel, kode uten SDK-en og den eldre
headeren.

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

## Leveringssemantikk

Disse semantikkene gjelder leveringer til **endepunkt**. Den eldre webhooken med én URL
er ett synkront forsøk uten gjenforsøk, med unntak av
konfigurasjonsutvekslingen for telefonsamtaler
[`telephony.incoming`](/nb/webhooks/call-incoming),
som forsøkes på nytt én gang.

<AccordionGroup>
  <Accordion title="Gjenforsøk">
    Hver hendelse forsøkes levert umiddelbart én gang. Ethvert `2xx`-svar
    bekrefter leveringen. Ved ethvert annet utfall (ikke-2xx,
    tilkoblingsfeil, tidsavbrudd) prøver vi på nytt etter **1 min, 5 min, 30 min, 2 t, 6 t,
    12 t og 24 t etter første forsøk** — 8 forsøk fordelt over
    24 timer. Hvis alle forsøk mislykkes, stopper leveringen, og endepunktet
    merkes med `status="failing"` i
    [webhook-endepunkter](/nb/webhooks/endpoints); vi sender e-post til eierne
    og administratorene i organisasjonen din og viser det i dashbordet. Returner `2xx` så snart
    nyttelasten er mottatt og lagret varig; behandle den asynkront.
    Testforsendelser fra endepunktets test-API får ett gjenforsøk omtrent ett minutt etter
    første forsøk og endrer aldri endepunktets status.
  </Accordion>

  <Accordion title="Rekkefølge">
    Leveringsrekkefølgen er etter beste evne. I praksis leverer vi i
    rekkefølgen hendelser sendes ut, men nye forsøk kan endre rekkefølgen ved feil.
    Dedupliser og avstem alltid etter `call_id` / objekt-ID.
  </Accordion>

  <Accordion title="Duplikater">
    Levering er **minst én gang**: et nytt forsøk etter et svar vi aldri
    mottok, kan duplisere en hendelse. Hvert nye forsøk har samme
    `event_id`, så lagre behandlede ID-er og hopp over gjentakelser. `event_id` er
    også delt på tvers av endepunkter — to endepunkter som abonnerer på den
    samme hendelsen, mottar samme `event_id`.
  </Accordion>

  <Accordion title="Tidsavbrudd">
    Leveringer til endepunkter har et tidsavbrudd på **30 s** per forsøk. På den
    eldre ruten er blokkerende forespørsler som styrer oppførselen til aktive samtaler — 
    konfigurasjonsutvekslingen
    [`telephony.incoming` / `web.incoming`](/nb/webhooks/call-incoming) —
    begrenset til **10 s** for en telefonsamtales
    første forsøk og **13 s** for hele utvekslingen, inkludert det ene
    gjenforsøket, og til **10 s** for en widgetøkt. Et tregt svar forsinker
    besvarelsen av samtalen, så sikt mot å svare innen et par sekunder. [Utsending av verktøykall](/nb/tools/overview) i webhook-modus tillater 20 s
    som standard, og verktøydeklarasjoner kan angi et overordnet `timeout`.
  </Accordion>

  <Accordion title="Kilde-IP-er">
    Utgående webhooks kommer fra ThunderPhones sky-IP-område.
    Hvis brannmuren din krever en tillatelsesliste, kontakt kundestøtte, så
    deler vi de gjeldende intervallene.
  </Accordion>
</AccordionGroup>

## Velge mellom eldre og endepunktbaserte webhooks

| Funksjon | Eldre (`/v1/webhook`) | Endepunkter (`/v1/developer/webhook-endpoints`) |
|---------|------------------------|----------------------------------------------|
| Antall URL-er | 1 per organisasjon | Mange per organisasjon |
| Hendelsesdekning | Kun `telephony.*` / `web.*` | Alle 10 hendelsestyper |
| Hendelsesfilter | — | Per endepunkt |
| Nye forsøk | Ingen, unntatt ett for utveksling av telefonanropskonfigurasjon | 8 forsøk over 24 t |
| Konvolutt | `type` + `data` | `type` + `data` + `event_id` |
| Rotering av hemmelighet | Erstatter én hemmelighet | Hemmelighet per endepunkt |
| Deaktiver uten å slette | `PUT /v1/webhook` med `{"url": ""}` | `status=disabled` |
| Statustilgjengelighet | — | `active` / `disabled` / `failing` |
| Blokkerende konfigurasjonsutveksling | Ja ([`telephony.incoming` / `web.incoming`](/nb/webhooks/call-incoming)) | Aldri — kun varsler |
| Best for | Dynamisk anropskonfigurasjon | Hendelsesmottak i produksjon |

Nye integrasjoner bør motta hendelser gjennom endepunktbaserte
webhooks. Behold (eller legg til) en eldre URL bare hvis du konfigurerer samtaler
dynamisk når de besvares, eller bruker verktøysdirigering i webhook-modus — disse
forespørsels-/svarutvekslingene kjører bare på den eldre ruten.

---

## Relatert

<CardGroup cols={2}>
  <Card title="Hendelseskatalog" icon="list" href="/nb/webhooks/events">
    Alle hendelsestyper og nyttelastene deres.
  </Card>
  <Card title="Webhook-endepunkter" icon="bolt" href="/nb/webhooks/endpoints">
    Administrer flere endepunkter, hendelsesfiltre og hemmeligheter.
  </Card>
  <Card title="telephony.incoming / web.incoming" icon="phone" href="/nb/webhooks/call-incoming">
    Den blokkerende forespørselen serveren din må svare på for å konfigurere samtaler.
  </Card>
  <Card title="telephony.complete / web.complete" icon="phone" href="/nb/webhooks/call-complete">
    Nyttelast etter samtalen med transkripsjon, opptak og beregninger.
  </Card>
</CardGroup>
