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

# Översikt över webhooks

> Så här levererar ThunderPhone händelser i realtid, så här verifierar du signaturer och så här skiljer sig de äldre och slutpunktsbaserade leveransmodellerna åt.

ThunderPhone skickar HTTP-`POST`-begäranden till din server när saker
händer under ett samtal — ett inkommande samtal startar, ett samtal avslutas, en granskningskörning slutförs, en avisering utlöses och så vidare. Det finns **två leveransmodeller**:

<CardGroup cols={2}>
  <Card title="Webhook-slutpunkter (rekommenderas)" icon="bolt" href="/sv/webhooks/endpoints">
    Flera URL:er, hemligheter per slutpunkt, händelsefilter per slutpunkt
    och automatiska återförsök.
    Hantera via `GET/POST/PATCH/DELETE /v1/developer/webhook-endpoints`.
  </Card>

  <Card title="Äldre webhook med en enda URL" icon="link" href="/api-reference/organizations#legacy-single-url-webhook">
    En URL per organisation. Innehåller samtalslivscykelhändelserna, inklusive
    de **blockerande** konfigurationsutbytena. Hanteras via `GET/PUT /v1/webhook`.
  </Card>
</CardGroup>

Alla tio händelsetyper i [händelsekatalogen](/sv/webhooks/events) levereras
via webhook-slutpunkter. De sex samtalslivscykelhändelserna
(`telephony.incoming`, `telephony.complete`, `telephony.tool`,
`web.incoming`, `web.complete`, `web.tool`) skickas **också** till den
äldre webhooken med en enda URL — om du har både en äldre URL och en
matchande slutpunkt får du händelsen på **båda** vägarna. Blockerande
beteende (konfigurationsutbytet för [`telephony.incoming` / `web.incoming`](/sv/webhooks/call-incoming)
och [verktygsdistribution](/sv/tools/overview) i
webhook-läge) finns endast på den äldre vägen; varje leverans till en
slutpunkt är en fire-and-forget-avisering.

## Nyttolastformat

Leveranser till slutpunkter är ett JSON-objekt med `data`, `event_id` och
`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` är unik för varje utsänd händelse. Den är identisk vid återförsök
**och** för varje slutpunkt som tar emot händelsen — deduplicera utifrån den.

Den äldre webhooken med en enda URL skickar samma `type` och `data`, men
**utan** `event_id`:

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

På nätet serialiseras varje brödtext kanoniskt — nycklar sorteras
alfabetiskt, utan blanksteg, UTF-8. De snyggt formaterade exemplen i
den här dokumentationen är endast för läsbarhet.

Se [händelsekatalogen](/sv/webhooks/events) för hela listan över händelsetyper
och nyttolastfält.

## Signaturverifiering

Varje begäran innehåller en HMAC-SHA256-signatur av den **råa
begärandetexten** i headern `X-ThunderPhone-Signature`. Signeringsnyckeln är
slutpunktens `secret` (eller din organisationsövergripande webhook-`secret`
för äldre leveranser).

### Steg

1. Läs den råa begärandetexten **före** eventuell parsning.
2. Beräkna `hmac_sha256(secret, body).hexdigest()`.
3. Jämför i konstant tid med headern `X-ThunderPhone-Signature`.

Vi signerar exakt de byte vi skickar, och dessa byte är den
kanoniska JSON-serialiseringen (sorterade nycklar, kompakta avgränsare). Därför
fungerar verifiering mot den råa texten alltid — och om ditt ramverk
bara ger dig parsad JSON ger omserialisering med sorterade nycklar och
kompakta avgränsare identiska byte. Båda metoderna beskrivs i
[verifieringsguiden](/sv/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>

## Leveranssemantik

Den här semantiken gäller leveranser till **ändpunkter**. Den äldre webhooken med en enda URL är ett enda synkront försök utan återförsök.

<AccordionGroup>
  <Accordion title="Återförsök">
    Varje händelse försöks levereras en gång omedelbart. Alla `2xx`-svar
    bekräftar leveransen. Vid alla andra utfall (icke-2xx,
    anslutningsfel, timeout) försöker vi igen efter **1 min, 5 min, 30 min, 2 h, 6 h,
    12 h och 24 h efter det första försöket** — 8 försök under
    24 timmar. Om alla försök misslyckas stoppas leveransen och ändpunkten
    markeras med `status="failing"` i
    [webhook-ändpunkter](/sv/webhooks/endpoints). Returnera `2xx` så snart som
    nyttolasten har tagits emot varaktigt; bearbeta asynkront.
  </Accordion>

  <Accordion title="Ordning">
    Leveransordningen är bästa möjliga. I praktiken levererar vi i den
    ordning händelser skickas, men återförsök kan ändra ordningen vid fel.
    Deduplicera alltid och stäm av efter `call_id` / objekt-id.
  </Accordion>

  <Accordion title="Dubbletter">
    Leveransen är **minst en gång**: ett återförsök efter ett svar som vi aldrig
    såg kan duplicera en händelse. Varje återförsök har samma
    `event_id`, så lagra bearbetade id:n och hoppa över upprepningar. `event_id` delas
    också mellan ändpunkter — två ändpunkter som prenumererar på samma händelse får samma `event_id`.
  </Accordion>

  <Accordion title="Timeoutar">
    Leveranser till ändpunkter har en timeout på **30 s** per försök. På den
    äldre sökvägen får blockerande begäranden som styr beteendet för aktiva samtal —
    konfigurationsutbytet för
    [`telephony.incoming` / `web.incoming`](/sv/webhooks/call-incoming) —
    timeout efter **10 s**, men ett långsamt svar fördröjer när samtalet besvaras,
    så sikta på att svara inom ett par sekunder. Verktygsdirigering i webhook-läge
    [tool dispatch](/sv/tools/overview) tillåter 20 s.
  </Accordion>

  <Accordion title="Käll-IP-adresser">
    Utgående webhooks kommer från ThunderPhones moln-IP-intervall.
    Om din brandvägg kräver en tillåtelselista kontaktar du supporten så
    delar vi de aktuella intervallen.
  </Accordion>
</AccordionGroup>

## Välja mellan äldre och ändpunktsbaserade webhooks

| Funktion                         | Äldre (`/v1/webhook`)                                                    | Ändpunkter (`/v1/developer/webhook-endpoints`) |
| -------------------------------- | ------------------------------------------------------------------------ | ---------------------------------------------- |
| Antal URL:er                     | 1 per organisation                                                       | Många per organisation                         |
| Händelsetäckning                 | Endast `telephony.*` / `web.*`                                           | Alla 10 händelsetyper                          |
| Händelsefilter                   | —                                                                        | Per ändpunkt                                   |
| Återförsök                       | Inga                                                                     | 8 försök under 24 h                            |
| Omslag                           | `type` + `data`                                                          | `type` + `data` + `event_id`                   |
| Hemlighetsrotation               | Ersätter en enda hemlighet                                               | Hemlighet per ändpunkt                         |
| Inaktivera utan att ta bort      | —                                                                        | `status=disabled`                              |
| Statussynlighet                  | —                                                                        | `active` / `disabled` / `failing`              |
| Blockerande konfigurationsutbyte | Ja ([`telephony.incoming` / `web.incoming`](/sv/webhooks/call-incoming)) | Aldrig — endast aviseringar                    |
| Passar bäst för                  | Dynamisk samtalskonfiguration                                            | Händelsehantering i produktion                 |

Nya integrationer bör hantera händelser via ändpunktsbaserade
webhooks. Behåll (eller lägg till) en äldre URL endast om du konfigurerar samtal
dynamiskt när de besvaras eller använder verktygsdirigering i webhook-läge — dessa
begäran/svar-utbyten körs endast på den äldre sökvägen.

***

## Relaterat

<CardGroup cols={2}>
  <Card title="Händelsekatalog" icon="list" href="/sv/webhooks/events">
    Alla händelsetyper och deras nyttolaster.
  </Card>

  <Card title="Webhook-ändpunkter" icon="bolt" href="/sv/webhooks/endpoints">
    Hantera flera ändpunkter, händelsefilter och hemligheter.
  </Card>

  <Card title="telephony.incoming / web.incoming" icon="phone" href="/sv/webhooks/call-incoming">
    Den blockerande begäran som din server måste besvara för att konfigurera samtal.
  </Card>

  <Card title="telephony.complete / web.complete" icon="phone" href="/sv/webhooks/call-complete">
    Nyttolast efter samtalet med transkription, inspelning och mätvärden.
  </Card>
</CardGroup>
