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

# Webhookien yleiskatsaus

> Miten ThunderPhone toimittaa reaaliaikaisia tapahtumia, miten allekirjoitukset vahvistetaan ja miten vanha sekä päätepistepohjainen toimitusmalli eroavat toisistaan.

ThunderPhone lähettää HTTP-`POST`-pyyntöjä palvelimellesi, kun puhelun aikana
tapahtuu jotain — saapuva puhelu alkaa, puhelu päättyy, arviointiajo
valmistuu, hälytys laukeaa ja niin edelleen. Toimitusmalleja on **kaksi**:

<CardGroup cols={2}>
  <Card title="Webhook-päätepisteet (suositellaan)" icon="bolt" href="/fi/webhooks/endpoints">
    Useita URL-osoitteita, päätepistekohtaiset salaisuudet, päätepistekohtaiset tapahtumasuodattimet
    ja automaattiset uudelleenyritykset.
    Hallitse kohdan `GET/POST/PATCH/DELETE /v1/developer/webhook-endpoints` kautta.
  </Card>

  <Card title="Yhden URL:n vanha webhook" icon="link" href="/api-reference/organizations#legacy-single-url-webhook">
    Yksi URL-osoite organisaatiota kohden. Sisältää puhelun elinkaaren tapahtumat, mukaan lukien
    **estävät** määritysvaihdot. Hallitaan kohdassa `GET/PUT /v1/webhook`.
  </Card>
</CardGroup>

Kaikki kymmenen [tapahtumaluettelon](/fi/webhooks/events) tapahtumatyyppiä
toimitetaan webhook-päätepisteiden kautta. Kuusi puhelun elinkaaren tapahtumaa
(`telephony.incoming`, `telephony.complete`, `telephony.tool`,
`web.incoming`, `web.complete`, `web.tool`) lähetetään **myös**
vanhaan yhden URL:n webhookiin — jos sinulla on sekä vanha URL että
vastaava päätepiste, vastaanotat tapahtuman **molempia** reittejä pitkin. Estävä
toiminta ([`telephony.incoming`- ja `web.incoming`-määritysvaihto](/fi/webhooks/call-incoming)
ja webhook-tilan [työkalun välitys](/fi/tools/overview))
on käytettävissä vain vanhalla reitillä; jokainen päätepistetoimitus on
ei-estävä ilmoitus.

## Hyötykuorman muoto

Päätepistetoimitukset ovat JSON-objekteja, joissa ovat `data`, `event_id` ja
`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` on yksilöllinen jokaiselle lähetetylle tapahtumalle. Se on sama
**sekä** uudelleenyrityksissä **että** kaikissa tapahtuman vastaanottavissa
päätepisteissä — käytä sitä duplikaattien poistoon.

Vanha yhden URL:n webhook lähettää saman `type`- ja `data`-sisällön mutta
**ilman** `event_id`-arvoa:

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

Verkossa jokainen runko serialisoidaan kanonisesti — avaimet lajitellaan
aakkosjärjestykseen, välilyöntejä ei käytetä ja merkistö on UTF-8. Näiden
dokumenttien muotoillut esimerkit ovat vain luettavuuden vuoksi.

Katso [tapahtumaluettelosta](/fi/webhooks/events) tapahtumatyyppien ja
hyötykuormakenttien täydellinen luettelo.

## Allekirjoituksen vahvistaminen

Jokainen pyyntö sisältää HMAC-SHA256-allekirjoituksen **raakapyynnön
rungosta** `X-ThunderPhone-Signature`-otsakkeessa. Allekirjoitusavain on
päätepisteen `secret` (tai organisaatiotason webhookin `secret`
vanhoille toimituksille).

### Vaiheet

1. Lue raakapyynnön runko **ennen** mitään jäsentämistä.
2. Laske `hmac_sha256(secret, body).hexdigest()`.
3. Vertaa sitä vakioaikaisesti `X-ThunderPhone-Signature`-otsakkeeseen.

Allekirjoitamme täsmälleen lähettämämme tavut, ja nämä tavut ovat
kanoninen JSON-sarjoitus (lajitellut avaimet, tiiviit erottimet). Siksi
raakapyyntöön perustuva vahvistus toimii aina — ja jos kehys antaa sinulle
vain jäsennetyn JSONin, sen uudelleensarjoittaminen lajitelluilla avaimilla
ja tiiviillä erottimilla tuottaa samat tavut. Molemmat menetelmät on
kuvattu [vahvistusoppaassa](/fi/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>

## Toimitussemantiikka

Nämä semantiikat koskevat **päätepisteisiin** tehtäviä toimituksia. Vanha yhden URL-osoitteen
webhook on yksi synkroninen yritys ilman uudelleenyrityksiä.

<AccordionGroup>
  <Accordion title="Uudelleenyritykset">
    Jokaista tapahtumaa yritetään toimittaa kerran välittömästi. Mikä tahansa `2xx`-vastaus
    vahvistaa toimituksen. Kaikissa muissa tilanteissa (muu kuin 2xx-vastaus,
    yhteysvirhe, aikakatkaisu) yritämme uudelleen **1 min, 5 min, 30 min, 2 h, 6 h,
    12 h ja 24 h ensimmäisen yrityksen jälkeen** — 8 yritystä
    24 tunnin aikana. Jos jokainen yritys epäonnistuu, toimitus pysähtyy ja päätepiste
    merkitään tilaan `status="failing"` kohdassa
    [webhook-päätepisteet](/fi/webhooks/endpoints). Palauta `2xx` heti, kun
    hyötykuorma on vastaanotettu pysyvästi; käsittele se asynkronisesti.
  </Accordion>

  <Accordion title="Järjestys">
    Toimitusjärjestys on parhaaseen pyrkivä. Käytännössä toimitamme tapahtumat siinä
    järjestyksessä kuin ne lähetetään, mutta uudelleenyritykset voivat muuttaa järjestystä epäonnistumisen
    yhteydessä. Deduplikoi ja täsmäytä aina `call_id`- / objektitunnuksen perusteella.
  </Accordion>

  <Accordion title="Kaksoiskappaleet">
    Toimitus on **vähintään kerran** -tyyppinen: uudelleenyritys vastauksen jälkeen, jota emme
    koskaan nähneet, voi tuottaa tapahtumasta kaksoiskappaleen. Jokaisessa uudelleenyrityksessä on sama
    `event_id`, joten tallenna käsitellyt tunnukset ja ohita toistot. `event_id` on
    myös yhteinen päätepisteiden välillä — kaksi samaa tapahtumaa tilaavaa päätepistettä
    vastaanottaa saman `event_id`:n.
  </Accordion>

  <Accordion title="Aikakatkaisut">
    Päätepistetoimituksissa on **30 s**:n aikakatkaisu yritystä kohden. Vanhassa polussa
    estävät pyynnöt, jotka ohjaavat aktiivisen puhelun toimintaa —
    [`telephony.incoming` / `web.incoming`](/fi/webhooks/call-incoming)
    -määritysvaihto — aikakatkaistaan **10 s** jälkeen, mutta hidas
    vastaus viivästyttää puheluun vastaamista, joten pyri vastaamaan muutamassa
    sekunnissa. Webhook-tilan [työkalujen välitys](/fi/tools/overview) sallii 20 s.
  </Accordion>

  <Accordion title="Lähde-IP-osoitteet">
    Lähtevät webhookit tulevat ThunderPhonen pilven IP-osoitealueelta.
    Jos palomuurisi edellyttää sallittujen osoitteiden luetteloa, ota yhteyttä tukeen, niin
    jaamme nykyiset alueet.
  </Accordion>
</AccordionGroup>

## Valinta vanhojen ja päätepistepohjaisten webhookien välillä

| Ominaisuus                  | Vanha (`/v1/webhook`)                                                       | Päätepisteet (`/v1/developer/webhook-endpoints`) |
| --------------------------- | --------------------------------------------------------------------------- | ------------------------------------------------ |
| URL-osoitteiden määrä       | 1 organisaatiota kohden                                                     | Useita organisaatiota kohden                     |
| Tapahtumakattavuus          | Vain `telephony.*` / `web.*`                                                | Kaikki 10 tapahtumatyyppiä                       |
| Tapahtumasuodatin           | —                                                                           | Päätepistekohtainen                              |
| Uudelleenyritykset          | Ei lainkaan                                                                 | 8 yritystä 24 tunnin aikana                      |
| Kuori                       | `type` + `data`                                                             | `type` + `data` + `event_id`                     |
| Salaisuuden kierto          | Korvaa yksittäisen salaisuuden                                              | Päätepistekohtainen salaisuus                    |
| Poista käytöstä poistamatta | —                                                                           | `status=disabled`                                |
| Tilan näkyvyys              | —                                                                           | `active` / `disabled` / `failing`                |
| Estävä määritysvaihto       | Kyllä ([`telephony.incoming` / `web.incoming`](/fi/webhooks/call-incoming)) | Ei koskaan — vain ilmoitukset                    |
| Sopii parhaiten             | Dynaaminen puhelumääritys                                                   | Tapahtumien käsittely tuotannossa                |

Uusien integraatioiden tulee vastaanottaa tapahtumia päätepistepohjaisten
webhookien kautta. Pidä (tai lisää) vanha URL-osoite vain, jos määrität puhelut
dynaamisesti puheluun vastaamisen yhteydessä tai käytät webhook-tilan työkalujen välitystä — nämä
pyyntö/vastausvaihdot toimivat vain vanhassa polussa.

***

## Aiheeseen liittyvää

<CardGroup cols={2}>
  <Card title="Tapahtumaluettelo" icon="list" href="/fi/webhooks/events">
    Kaikki tapahtumatyypit ja niiden hyötykuormat.
  </Card>

  <Card title="Webhook-päätepisteet" icon="bolt" href="/fi/webhooks/endpoints">
    Hallitse useita päätepisteitä, tapahtumasuodattimia ja salaisuuksia.
  </Card>

  <Card title="telephony.incoming / web.incoming" icon="phone" href="/fi/webhooks/call-incoming">
    Estävä pyyntö, johon palvelimesi on vastattava puheluiden määrittämiseksi.
  </Card>

  <Card title="telephony.complete / web.complete" icon="phone" href="/fi/webhooks/call-complete">
    Puhelun jälkeinen hyötykuorma, joka sisältää litteroinnin, tallenteen ja mittarit.
  </Card>
</CardGroup>
