---
title: "telephony.incoming / web.incoming"
description: "Webhook bloccante che definisce in tempo reale la configurazione di una chiamata in entrata."
---

Quando una chiamata telefonica in arrivo raggiunge un numero **senza un
agente assegnato**, oppure una sessione di widget web viene avviata su una chiave pubblicabile in
`mode="webhook"`, ThunderPhone invia una richiesta **bloccante**
`telephony.incoming` / `web.incoming` al tuo
[URL webhook legacy](/api-reference/organizations#legacy-single-url-webhook)
e attende una risposta di configurazione: fino a **13 secondi** per una
chiamata telefonica, incluso un tentativo aggiuntivo, e fino a **10 secondi** per una sessione
del widget (vedi [Timeout e tentativi aggiuntivi](#timeouts-and-retries)). Usa questo
scambio per scegliere dinamicamente un prompt, una voce e gli strumenti per ogni chiamata —
consulta la [guida alla configurazione dinamica delle chiamate](/it/guides/dynamic-call-config)
per il modello end-to-end.

<Note>
  Gli [endpoint webhook](/it/webhooks/endpoints) sottoscritti ricevono anch'essi
  `telephony.incoming` / `web.incoming` — per **ogni** chiamata in entrata
  e sessione web, configurata con un agente o meno — ma queste consegne sono
  notifiche fire-and-forget con un `event_id`, mai bloccanti.
  Solo il webhook legacy a URL singolo gestisce lo scambio di configurazione
  descritto in questa pagina. Le strutture delle notifiche degli endpoint sono nel
  [catalogo eventi](/it/webhooks/events).
</Note>

## Timeout e nuovi tentativi

Rispondi rapidamente: il chiamante è in attesa in linea mentre decidi.

Per una chiamata telefonica (`telephony.incoming`):

- Il primo tentativo può richiedere fino a **10 secondi**.
- Se va in timeout, non riesce a connettersi o riceve una risposta
  `5xx`, `408` o `429`, ThunderPhone prova un'altra volta. Il nuovo tentativo riceve il tempo
  rimanente di un budget di **13 secondi** per l'intero scambio: circa 3 secondi dopo
  un primo tentativo andato in timeout, quasi tutti i 13 secondi dopo un errore rapido.
  Non viene effettuato alcun nuovo tentativo se rimarrebbero meno di 2 secondi.
- Qualsiasi altra risposta `4xx`, una risposta oltre il
  [limite di dimensione](#response-size-limit) o una `2xx` il cui corpo non contiene una
  configurazione valida è definitiva e non viene ritentata.
- Entrambi i tentativi inviano lo stesso corpo, lo stesso `X-ThunderPhone-Signature`
  e la stessa intestazione `X-ThunderPhone-Idempotency-Key`
  (`telephony-incoming-<call_id>`), così un gestore con effetti collaterali
  può riconoscere il nuovo tentativo.

Non è disponibile alcun agente di fallback. Se non arriva una configurazione utilizzabile, la
chiamata viene terminata subito anziché lasciare il chiamante su una linea
silenziosa. Nella cronologia chiamate appare come chiamata non fatturata non riuscita con
`end_reason` `inbound_config_webhook_failed` e viene inviato il consueto evento
[`telephony.complete`](/it/webhooks/call-complete).

Una sessione widget (`web.incoming`) riceve un solo tentativo fino a 10
secondi. Se non riesce, la richiesta della sessione non riesce con `502` o `422`.

<Warning>
  **Le chiamate configurate tramite webhook non includono alcun annuncio di consenso
  di ThunderPhone.** Le chiamate configurate tramite questo scambio aggirano
  l'annuncio all'avvio della chiamata a livello di agente e sono espressamente escluse dal
  framework di ThunderPhone per gli annunci di consenso (Termini di servizio,
  sezione "Registrazione e consenso"). La tua organizzazione è l'unica
  responsabile di ogni avviso e consenso relativo a registrazione, monitoraggio,
  partecipazione dell'IA e identificazione del chiamante richiesto per queste chiamate —
  le chiamate possono comunque essere registrate, trascritte, analizzate e gestite
  dall'IA. Integra le informative richieste nel tuo flusso di chiamata prima di
  abilitare questo percorso.
</Warning>

## Payload della richiesta

Per le chiamate telefoniche (`telephony.incoming`):

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

| Campo | Tipo | Descrizione |
|-------|------|-------------|
| `call_id` | integer | ID della chiamata — stabile in tutti gli eventi di questa chiamata |
| `from_number` | string | Numero del chiamante in formato E.164 |
| `to_number` | string | Destinazione E.164 (uno dei tuoi numeri ThunderPhone) |

Per le sessioni del widget web (`web.incoming`), `data` identifica la
pagina di incorporamento anziché i numeri telefonici:

```json
{
  "type": "web.incoming",
  "data": {
    "call_id": 987654322,
    "origin_domain": "https://example.com",
    "publishable_key_prefix": "pk_live_a1b2"
  }
}
```

| Campo | Tipo | Descrizione |
|-------|------|-------------|
| `call_id` | integer | ID chiamata |
| `origin_domain` | string | Origine della pagina che ospita il widget, come inviata dal browser. Un client non browser può inviare qualsiasi valore, quindi non trattarlo come prova della provenienza della richiesta |
| `publishable_key_prefix` | string | Primi caratteri della chiave pubblicabile che ha aperto la sessione |
| `language`, `primary_language` | string | Presente quando la sessione del widget ha richiesto un override della lingua |
| `voice` | string | Presente quando la sessione del widget ha richiesto un override della voce |
| `website_context` | string | Presente quando il widget ha passato il contesto della pagina per sessione |

I campi di override (`language`, `voice`, `website_context` e qualsiasi
`variables` del browser) arrivano solo quando la chiave pubblicabile li consente nei
suoi [override del browser](/it/guides/embed-web-widget). Una
richiesta di sessione che contiene un override non consentito dalla chiave viene rifiutata prima che questo webhook venga chiamato.

<Note>
  I widget in modalità webhook inviano questa richiesta al proprio
  `webhook_url` della chiave pubblicabile quando è impostato, altrimenti usano l'URL
  webhook a livello di organizzazione. In entrambi i casi, la richiesta è firmata con il
  `secret` del webhook dell'organizzazione.
</Note>

---

## Schema della risposta

Restituisci un oggetto JSON che descriva la configurazione dell'agente per questa chiamata.
`prompt` e `voice` sono obbligatori; tutto il resto è facoltativo.

```json
{
  "prompt":  "You are a helpful booking assistant for Acme Restaurant.",
  "voice":   "john",
  "product": "spark",
  "background_track": null,
  "tools":   []
}
```

| Campo | Tipo | Obbligatorio | Descrizione |
|-------|------|----------|-------------|
| `prompt` | string | sì | Prompt di sistema che guida l'agente |
| `voice` | string | sì | ID della voce da [`GET /v1/voices`](/api-reference/agents#voices), ad es. `john`. `voice_name` è accettato come alias. Le voci sconosciute non superano la convalida e rifiutano la chiamata |
| `product` | string | no | Predefinito: `spark`. Valori consentiti: `spark`, `bolt`, `storm-base`, `storm-base-with-ack`, `storm-extra`, `storm-extra-with-ack` |
| `thinking_level` | string | no | `minimal`, `base` (predefinito) o `extra`. Sovrascritto per i prodotti Storm: `storm-extra*` impone `extra`, gli altri `storm-*` impongono `base` |
| `audio_context_mode` | string | no | `full` (predefinito) o `reduced` |
| `watchdog_enabled` | boolean | no | Abilita la supervisione per questa chiamata. Predefinito: `false` |
| `additional_audio_context` | boolean \| null | no | Include gli ultimi turni dell'audio del chiamante anziché solo il turno più recente, migliorando le correzioni e la raccolta di dati ricchi di ortografia/numeri, con un lieve impatto su latenza e costo. È attivo per impostazione predefinita nelle sessioni in entrata e disattivo nelle chiamate telefoniche in uscita; `null` mantiene il valore predefinito |
| `storm_feedback_mode` | string | no | `none`, `acknowledgement` (predefinito) o `tick` |
| `language` | string | no | Abbreviazione di `primary_language` |
| `primary_language` | string | no | Codice lingua, normalizzato (predefinito `en`). I codici non risolvibili rifiutano la chiamata |
| `has_additional_languages` | boolean | no | Predefinito: `false` |
| `additional_languages` | array di string | no | Lingue aggiuntive a cui l'agente può passare |
| `native_voice_switching` | boolean | no | Predefinito: `false`. Quando la chiamata passa a un'altra lingua, sostituisce la voce configurata con una voce madrelingua per quella lingua, abbinata per genere |
| `language_switching` | string \| null | no | `ask_first` (predefinito) o `follow_caller`; `null` equivale a `ask_first`. Definisce come un agente multilingue gestisce l'altra persona che parla un'altra delle sue lingue. `ask_first` passa subito quando la persona richiede la lingua o pronuncia una frase completa in essa e chiede se continuare in quella lingua dopo qualcosa di breve, come un "sì". `follow_caller` passa a qualsiasi lingua configurata parlata dalla persona, anche per una sola parola. Si applica solo quando è impostato `additional_languages` |
| `background_track` | string \| null | no | ID dell'audio ambientale o `null` |
| `acknowledgement_prompt_mode` | string | no | `auto` (predefinito) o `manual` (prodotti Storm-with-ack) |
| `acknowledgement_prompt` | string | no | Utilizzato quando `acknowledgement_prompt_mode="manual"` |
| `silence_interval_seconds` | integer \| null | no | 5–120. Secondi di silenzio del chiamante prima di una richiesta di conferma |
| `silence_max_checkins` | integer \| null | no | 1–10 |
| `silence_checkins_enabled` | boolean | no | Predefinito: `true` |
| `connect_tone_enabled` | boolean | no | Predefinito: `false` |
| `pronunciation_dicts` | boolean \| null | no | Predefinito: `false`; `null` equivale a `false`. Quando è `true`, la sintesi vocale Cartesia della chiamata utilizza il dizionario di pronuncia ThunderPhone per la lingua della chiamata, se disponibile. Funziona per le chiamate telefoniche e del widget. Si applica solo alle voci Cartesia di ThunderPhone, non alle voci associate alla tua chiave API Cartesia. Non viene utilizzato con `agent_id`, per il quale si applicano le impostazioni dell'agente salvato |
| `voicemail_action` | string | no | `prompt` (predefinito), `hangup` o `message` |
| `voicemail_message` | string | no | Utilizzato quando `voicemail_action="message"` |
| `agent_name` | string | no | Nome visualizzato riportato nelle dashboard e nel widget |
| `org_name` | string | no | Nome visualizzato dell'organizzazione per la persona dell'agente |
| `tools` | array | no | Schemi inline degli strumenti funzione (vedi [Strumenti funzione](/it/tools/overview)) |
| `transfer_destinations` | array di object \| null | no | Fino a 50 voci `{name, number}` a cui questa chiamata può essere trasferita, convalidate come i [numeri di trasferimento](/api-reference/agents#transfer-numbers) di un agente. L'agente trasferisce in base al nome e solo verso queste destinazioni; `[]` indica nessun trasferimento. Ometti il campo o invia `null` per consentire all'agente di trasferire a un numero scelto da lui |
| `timezone` | string | no | Fuso orario IANA per questa chiamata, ad esempio `America/Chicago`. L'agente comunica la data e l'ora in questo fuso, anziché nel proprio fuso orario. Consentito anche accanto a `agent_id`. I valori che non sono fusi orari IANA vengono ignorati. Vedi [Imposta il fuso orario per chiamata](/it/guides/dynamic-call-config) |
| `call_id` | integer | no | Eco facoltativo dell'ID chiamata della richiesta; ignorato |

<Note>
  Le chiavi di primo livello sconosciute vengono **ignorate** silenziosamente: un nome di campo con un errore di battitura
  non rifiuta la configurazione, semplicemente non viene applicato. L'ordine di parola
  e `max_hold_seconds` non sono accettati qui; sono configurabili solo
  sull'[Agente](/api-reference/agents) stesso.
</Note>

Poiché `prompt` e `voice` sono obbligatori, restituire `{}` o qualsiasi
risposta che non superi la convalida rifiuta la chiamata: una chiamata telefonica viene terminata
come descritto in [Timeout e tentativi](#timeouts-and-retries), e una
richiesta di sessione del widget non va a buon fine con `422`. Non è disponibile alcun fallback
a un agente statico in questo percorso (un numero o una chiave in modalità webhook non ha un agente
assegnato).

---

## Limite delle dimensioni della risposta

<Warning>
  Le risposte di configurazione sono limitate a **5 MiB**. Se un gestore
  restituisce una risposta più grande, anche con uno stato `2xx`,
  ThunderPhone segnala che la risposta ha superato il limite e
  rifiuta la chiamata o la sessione del widget. Limita la risposta ai campi
  necessari per la configurazione della chiamata; ospita i dati di grandi dimensioni dietro strumenti funzione o
  un altro servizio invece di incorporarli nella configurazione.
</Warning>

---

## Esempio di gestore

<CodeGroup>
```python Python (FastAPI)
import hashlib
import hmac
import json
import os

from fastapi import FastAPI, HTTPException, Request

app = FastAPI()
WEBHOOK_SECRET = os.environ["THUNDERPHONE_WEBHOOK_SECRET"]

def verify(body: bytes, signature: str) -> bool:
    expected = hmac.new(WEBHOOK_SECRET.encode(), body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, signature or "")

@app.post("/thunderphone-webhook")
async def webhook(request: Request):
    body = await request.body()
    if not verify(body, request.headers.get("X-ThunderPhone-Signature", "")):
        raise HTTPException(status_code=401)

    event = json.loads(body)
    if event["type"] == "telephony.incoming":
        caller = event["data"]["from_number"]
        prompt = (
            "Greet the caller as a San Francisco local…"
            if caller.startswith("+1415")
            else "You are a friendly customer support agent…"
        )
        return {
            "prompt": prompt,
            "voice": "john",
            "product": "spark",
        }
    if event["type"] == "web.incoming":
        return {
            "prompt": "You are the website's helpful voice assistant…",
            "voice": "john",
            "product": "spark",
        }
    return {}
```

```javascript Node.js (Express)
import crypto from "node:crypto";
import express from "express";

const app = express();
const SECRET = process.env.THUNDERPHONE_WEBHOOK_SECRET;

function verify(body, signature) {
  const expected = crypto
    .createHmac("sha256", SECRET)
    .update(body)
    .digest("hex");
  return signature &&
    crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signature));
}

app.post(
  "/thunderphone-webhook",
  express.raw({ type: "application/json" }),
  (req, res) => {
    if (!verify(req.body, req.header("X-ThunderPhone-Signature"))) {
      return res.sendStatus(401);
    }
    const event = JSON.parse(req.body.toString("utf8"));

    if (event.type === "telephony.incoming" || event.type === "web.incoming") {
      const caller = event.data.from_number || "web";
      const prompt = caller.startsWith("+1415")
        ? "Greet the caller as a San Francisco local…"
        : "You are a friendly customer support agent…";
      return res.json({
        prompt,
        voice: "john",
        product: "spark",
      });
    }
    res.json({});
  },
);
```
</CodeGroup>

---

## Risposta con strumenti funzione

Allega strumenti affinché l'IA possa chiamare le tue API durante la conversazione:

```json
{
  "prompt":  "You are a booking assistant. Use the available tools to help customers schedule appointments.",
  "voice":   "john",
  "product": "spark",
  "tools": [
    {
      "type": "function",
      "function": {
        "name": "search_appointments",
        "description": "Find available appointment slots",
        "parameters": {
          "type": "object",
          "properties": {
            "date": { "type": "string", "description": "YYYY-MM-DD" },
            "service": { "type": "string" }
          },
          "required": ["date"]
        }
      },
      "endpoint": {
        "url": "https://api.example.com/appointments/search",
        "method": "POST",
        "headers": {
          "X-Api-Key": "your-key"
        }
      }
    }
  ]
}
```

<Tip>
  Le richieste agli endpoint degli strumenti sono firmate con lo **stesso segreto
  webhook dell'organizzazione** che ha firmato questo scambio. Consulta
  [Strumenti funzione](/it/tools/overview) per la struttura esatta e il formato della
  richiesta firmata.
</Tip>

---

## Scheda rapida dei livelli di prodotto

| Prodotto | Latenza | Ragionamento | Conferma |
|---------|---------|-----------|-----------------|
| `spark` | Minima | Base | — |
| `bolt` | Bassa | Migliorato | — |
| `storm-base` | Media | Solido | — |
| `storm-base-with-ack` | Media | Solido | Riempitivo automatico durante l'elaborazione |
| `storm-extra` | Più alta | Approfondito | — |
| `storm-extra-with-ack` | Più alta | Approfondito | Riempitivo automatico durante l'elaborazione |

---

## Correlati

<CardGroup cols={2}>
  <Card title="telephony.complete / web.complete" icon="phone-slash" href="/it/webhooks/call-complete">
    L'evento di fine chiamata non bloccante.
  </Card>
  <Card title="Strumenti funzione" icon="screwdriver-wrench" href="/it/tools/overview">
    Schema JSON completo per `tools[]` e contratto dell'endpoint firmato.
  </Card>
  <Card title="Endpoint webhook" icon="bolt" href="/it/webhooks/endpoints">
    Iscrivi più URL a `telephony.incoming` / `web.incoming`.
  </Card>
  <Card title="Configurazione dinamica delle chiamate" icon="wand-magic-sparkles" href="/it/guides/dynamic-call-config">
    Modelli per prompt, strumenti e test A/B per chiamante.
  </Card>
</CardGroup>
