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

# Oversikt over webhooks

> Slik leverer ThunderPhone hendelser i sanntid, slik bekrefter 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, et anrop 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 anropets livssyklus, inkludert
    de **blokkerende** konfigurasjonsutvekslingene. Administreres på `GET/PUT /v1/webhook`.
  </Card>
</CardGroup>

Alle de ti hendelsestypene i [hendelseskatalogen](/nb/webhooks/events)
leveres gjennom webhook-endepunkter. De seks hendelsene i anropets 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
samsvarende endepunkt, mottar du hendelsen på **begge** banene. Blokkerende
atferd (konfigurasjonsutvekslingen for [`telephony.incoming` / `web.incoming`](/nb/webhooks/call-incoming)
og [verktøyskøyring](/nb/tools/overview) i webhook-modus)
finnes utelukkende på den eldre banen; hver levering til et endepunkt er et
fire-and-forget-varsel.

## Nyttelastformat

Leveringer til endepunkter er et JSON-objekt med `data`, `event_id` og
`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` er unik for hver utløste hendelse. Den er identisk på tvers av nye forsøk
**og** på tvers av hvert endepunkt som mottar hendelsen — bruk den til deduplisering.

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

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

Ved overføring serialiseres hver body kanonisk — nøkler sortert
alfabetisk, uten mellomrom, UTF-8. De pent formaterte eksemplene 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 en HMAC-SHA256-signatur over den **rå
forespørselsbrødteksten** i `X-ThunderPhone-Signature`-headeren. Signeringsnøkkelen er
endepunktets `secret` (eller organisasjonens webhook-`secret` for eldre
leveringer).

### Trinn

1. Les den rå forespørselsbrødteksten **før** eventuell parsing.
2. Beregn `hmac_sha256(secret, body).hexdigest()`.
3. Sammenlign i konstant tid med `X-ThunderPhone-Signature`-headeren.

Vi signerer nøyaktig byteene vi sender, og disse byteene er den
kanoniske JSON-serialiseringen (sorterte nøkler, kompakte skilletegn). Derfor
fungerer verifisering mot den rå brødteksten alltid — og hvis rammeverket ditt
bare gir deg parsede JSON-data, produserer reserialisering med sorterte nøkler og
kompakte skilletegn identiske byte. Begge fremgangsmåtene er beskrevet i
[verifiseringsveiledningen](/nb/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>

## Leveringssemantikk

Disse semantikkene gjelder leveringer til **endepunkter**. Den eldre webhooken med én URL
er ett enkelt synkront forsøk uten nye forsøk.

<AccordionGroup>
  <Accordion title="Nye forsøk">
    Hver hendelse forsøkes levert én gang umiddelbart. Ethvert `2xx`-svar
    bekrefter leveringen. Ved ethvert annet utfall (ikke-2xx,
    tilkoblingsfeil, tidsavbrudd) forsøker vi på nytt **1 min, 5 min, 30 min, 2 t, 6 t,
    12 t og 24 t etter første forsøk** — 8 forsøk over
    24 timer. Hvis hvert forsøk mislykkes, stopper leveringen, og endepunktet
    merkes med `status="failing"` i
    [webhook-endepunkter](/nb/webhooks/endpoints). Returner `2xx` så snart
    nyttelasten er varig akseptert; behandle den asynkront.
  </Accordion>

  <Accordion title="Rekkefølge">
    Leveringsrekkefølge er basert på beste innsats. I praksis leverer vi i
    rekkefølgen hendelsene sendes ut, men nye forsøk kan endre rekkefølgen ved feil.
    Fjern alltid duplikater og avstem 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 banen får blokkerende forespørsler som styrer atferd under aktive samtaler —
    utvekslingen av konfigurasjon for
    [`telephony.incoming` / `web.incoming`](/nb/webhooks/call-incoming) —
    tidsavbrudd etter **10 s**, men et tregt svar forsinker besvaring av samtalen,
    så prøv å svare innen et par sekunder. [Verktøyutsending](/nb/tools/overview) i webhook-modus tillater 20 s.
  </Accordion>

  <Accordion title="Kilde-IP-adresser">
    Utgående webhooks kommer fra ThunderPhones sky-IP-område.
    Hvis brannmuren din krever en tillatelsesliste, kontakt kundestøtte, så
    deler vi de gjeldende områdene.
  </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                                                                    | 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              | —                                                                        | `status=disabled`                               |
| Synlighet av status                  | —                                                                        | `active` / `disabled` / `failing`               |
| Blokkerende konfigurasjonsutveksling | Ja ([`telephony.incoming` / `web.incoming`](/nb/webhooks/call-incoming)) | Aldri — kun varsler                             |
| Best for                             | Dynamisk samtalekonfigurasjon                                            | Hendelseshåndtering i produksjon                |

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

***

## 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 måltall.
  </Card>
</CardGroup>
