---
title: "Panoramica dei webhook"
description: "Come ThunderPhone invia eventi in tempo reale, come verificare le firme e come si confrontano i modelli di consegna legacy e basati su endpoint."
---

ThunderPhone invia richieste HTTP `POST` al tuo server quando si verificano
eventi durante una chiamata: inizia una chiamata in entrata, termina una chiamata, viene completata
un'esecuzione di valutazione, viene attivato un avviso e così via. Esistono **due modelli
di consegna**:

<CardGroup cols={2}>
  <Card title="Endpoint webhook (consigliati)" icon="bolt" href="/it/webhooks/endpoints">
    Più URL, secret per endpoint, filtri evento per endpoint
    e tentativi automatici.
    Gestiscili tramite `GET/POST/PATCH/DELETE /v1/developer/webhook-endpoints`.
  </Card>
  <Card title="Webhook legacy a URL singolo" icon="link" href="/api-reference/organizations#legacy-single-url-webhook">
    Un URL per organizzazione. Trasporta gli eventi del ciclo di vita della chiamata, inclusi gli
    scambi di configurazione **bloccanti**. Gestito tramite `GET/PUT /v1/webhook`.
  </Card>
</CardGroup>

Tutti i tipi di evento elencati nel [catalogo eventi](/it/webhooks/events) vengono
consegnati tramite endpoint webhook. I sei eventi del ciclo di vita della chiamata
(`telephony.incoming`, `telephony.complete`, `telephony.tool`,
`web.incoming`, `web.complete`, `web.tool`) vengono **inoltre** inviati al
webhook legacy a URL singolo: se hai sia un URL legacy sia un endpoint
corrispondente, ricevi l'evento su **entrambi** i percorsi. Il comportamento bloccante
(lo [scambio di configurazione `telephony.incoming` / `web.incoming`](/it/webhooks/call-incoming)
e l'[instradamento degli strumenti](/it/tools/overview) in modalità webhook)
risiede esclusivamente nel percorso legacy; ogni consegna a un endpoint è una
notifica fire-and-forget.

## Formato del payload

Le consegne agli endpoint sono un oggetto JSON con `data`, `event_id` e
`type`:

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

`event_id` è univoco per ogni evento emesso. È identico tra i tentativi
**e** tra tutti gli endpoint che ricevono l'evento: usalo per la deduplicazione.

Il webhook legacy a URL singolo invia gli stessi `type` e `data`, ma
**senza** `event_id`:

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

In transito, ogni body viene serializzato in modo canonico: chiavi ordinate
alfabeticamente, nessuno spazio vuoto, UTF-8. Gli esempi formattati nelle
presenti documentazioni sono solo a scopo di leggibilità.

Consulta il [catalogo eventi](/it/webhooks/events) per l'elenco completo dei tipi di evento
e dei campi del payload.

## Verifica della firma

Ogni richiesta include due firme, entrambe HMAC-SHA256 con chiave nel
`secret` dell'endpoint (oppure nel `secret` del webhook a livello di organizzazione per le
consegne legacy):

- `X-ThunderPhone-Signature-V2: t=<unix seconds>,v1=<hex>` firma il
  timestamp, il metodo, il percorso e la query, nonché il corpo non elaborato. Verifica
  questa firma e rifiuta le richieste il cui `t` dista più di **5 minuti**
  dal tuo orologio, in modo che una richiesta intercettata non possa essere riprodotta in seguito. I tentativi vengono
  firmati di nuovo quando vengono inviati.
- `X-ThunderPhone-Signature` firma solo il corpo non elaborato. Viene comunque inviata,
  invariata, affinché i verificatori esistenti continuino a funzionare.

### Passaggi

1. Leggi l'header, il metodo, il percorso e la query non elaborati e il
   corpo non elaborato **prima** di qualsiasi analisi.
2. Calcola l'HMAC-SHA256 esadecimale di
   `<t>\n<METHOD>\n<path and query>\n<body>` con il tuo segreto.
3. Rifiuta la richiesta se `t` differisce di oltre 300 secondi o se il
   risultato non corrisponde a `v1` in un confronto a tempo costante.

I corpi sono la serializzazione JSON canonica (chiavi ordinate, separatori
compatti) e firmiamo esattamente i byte che trasmettiamo. La
[guida alla verifica](/it/guides/verify-webhook-signatures) contiene la stringa
firmata esatta, un esempio completo, codice senza SDK e l'header legacy.

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

## Semantica di consegna

Queste semantiche si applicano alle consegne degli **endpoint**. Il webhook legacy con un singolo URL
effettua un unico tentativo sincrono senza nuovi tentativi, fatta eccezione per lo scambio di configurazione
delle chiamate telefoniche [`telephony.incoming`](/it/webhooks/call-incoming),
che viene ritentato una volta.

<AccordionGroup>
  <Accordion title="Nuovi tentativi">
    Ogni evento viene tentato immediatamente una volta. Qualsiasi risposta `2xx`
    conferma la consegna. In caso di qualsiasi altro esito (non-2xx,
    errore di connessione, timeout), ritentiamo dopo **1 m, 5 m, 30 m, 2 h, 6 h,
    12 h e 24 h dal primo tentativo** — 8 tentativi nell'arco di
    24 ore. Se tutti i tentativi falliscono, la consegna si interrompe e l'endpoint
    viene contrassegnato con `status="failing"` negli
    [endpoint webhook](/it/webhooks/endpoints); inviamo un'email ai
    proprietari e agli amministratori della tua organizzazione e lo mostriamo nella dashboard. Restituisci `2xx` non appena
    il payload viene accettato in modo duraturo; elaboralo in modo asincrono.
    Gli invii di test dall'API di test dell'endpoint ricevono un nuovo tentativo circa un minuto dopo
    il primo tentativo e non modificano mai lo stato dell'endpoint.
  </Accordion>

  <Accordion title="Ordinamento">
    L'ordinamento delle consegne è best-effort. In pratica consegniamo gli eventi
    nell'ordine in cui vengono emessi, ma i ritentativi possono modificarne l'ordine in caso di errore.
    Esegui sempre deduplicazione e riconciliazione tramite `call_id` / id oggetto.
  </Accordion>

  <Accordion title="Duplicati">
    La consegna è **almeno una volta**: un ritentativo dopo una risposta che non abbiamo
    ricevuto può duplicare un evento. Ogni ritentativo contiene lo stesso
    `event_id`, quindi memorizza gli id elaborati e ignora le ripetizioni. `event_id` è
    inoltre condiviso tra gli endpoint — due endpoint iscritti allo
    stesso evento ricevono lo stesso `event_id`.
  </Accordion>

  <Accordion title="Timeout">
    Le consegne degli endpoint hanno un timeout di **30 s** per tentativo. Nel
    percorso legacy, le richieste bloccanti che determinano il comportamento delle chiamate in corso — lo
    scambio di configurazione [`telephony.incoming` / `web.incoming`](/it/webhooks/call-incoming)
    — sono limitate a **10 s** per il primo tentativo di una chiamata telefonica
    e a **13 s** per l'intero scambio, incluso l'unico
    nuovo tentativo, e a **10 s** per una sessione widget. Una risposta lenta ritarda
    la risposta alla chiamata, quindi cerca di rispondere entro un paio di secondi. L'[invio di strumenti](/it/tools/overview) in modalità webhook consente 20 s
    per impostazione predefinita e le dichiarazioni degli strumenti possono impostare un `timeout` di primo livello.
  </Accordion>

  <Accordion title="IP di origine">
    I webhook in uscita provengono dall'intervallo IP cloud di ThunderPhone.
    Se il tuo firewall richiede un elenco di autorizzazione, contatta il supporto e
    condivideremo gli intervalli attuali.
  </Accordion>
</AccordionGroup>

## Scelta tra webhook legacy e basati su endpoint

| Funzionalità | Legacy (`/v1/webhook`) | Endpoint (`/v1/developer/webhook-endpoints`) |
|---------|------------------------|----------------------------------------------|
| Numero di URL | 1 per organizzazione | Molti per organizzazione |
| Copertura degli eventi | Solo `telephony.*` / `web.*` | Tutti i 10 tipi di evento |
| Filtro eventi | — | Per endpoint |
| Tentativi | Nessuno, tranne uno per lo scambio di configurazione della chiamata telefonica | 8 tentativi nell'arco di 24 h |
| Busta | `type` + `data` | `type` + `data` + `event_id` |
| Rotazione del segreto | Sostituisce il singolo segreto | Segreto per endpoint |
| Disabilitare senza eliminare | `PUT /v1/webhook` con `{"url": ""}` | `status=disabled` |
| Visibilità dello stato | — | `active` / `disabled` / `failing` |
| Scambio di configurazione bloccante | Sì ([`telephony.incoming` / `web.incoming`](/it/webhooks/call-incoming)) | Mai — solo notifiche |
| Ideale per | Configurazione dinamica delle chiamate | Elaborazione degli eventi in produzione |

Le nuove integrazioni dovrebbero consumare gli eventi tramite webhook
basati su endpoint. Mantieni (o aggiungi) un URL legacy solo se configuri le chiamate
dinamicamente al momento della risposta o utilizzi l'invio di strumenti in modalità webhook — questi
scambi richiesta/risposta vengono eseguiti solo sul percorso legacy.

---

## Correlati

<CardGroup cols={2}>
  <Card title="Catalogo degli eventi" icon="list" href="/it/webhooks/events">
    Tutti i tipi di evento e i relativi payload.
  </Card>
  <Card title="Endpoint webhook" icon="bolt" href="/it/webhooks/endpoints">
    Gestisci più endpoint, filtri degli eventi e segreti.
  </Card>
  <Card title="telephony.incoming / web.incoming" icon="phone" href="/it/webhooks/call-incoming">
    La richiesta bloccante a cui il tuo server deve rispondere per configurare le chiamate.
  </Card>
  <Card title="telephony.complete / web.complete" icon="phone" href="/it/webhooks/call-complete">
    Payload post-chiamata con trascrizione, registrazione e metriche.
  </Card>
</CardGroup>
