---
title: "telephony.incoming / web.incoming"
description: "Blockierender Webhook, der die Konfiguration eines eingehenden Anrufs in Echtzeit festlegt."
---

Wenn ein eingehender Anruf eine Nummer **ohne zugewiesenen
Agenten** erreicht oder eine Web-Widget-Sitzung mit einem veröffentlichbaren Schlüssel im
`mode="webhook"` startet, sendet ThunderPhone eine **blockierende**
`telephony.incoming` / `web.incoming`-Anfrage an Ihre
[Legacy-Webhook-URL](/api-reference/organizations#legacy-single-url-webhook)
und wartet auf eine Konfigurationsantwort: bis zu **13 Sekunden** für einen
Anruf, einschließlich eines Wiederholungsversuchs, und bis zu **10 Sekunden** für eine
Widget-Sitzung (siehe [Zeitüberschreitungen und Wiederholungsversuche](#timeouts-and-retries)). Verwenden Sie diesen
Austausch, um Prompt, Stimme und Tools für jeden Anruf dynamisch auszuwählen —
das vollständige Muster finden Sie im [Leitfaden zur dynamischen Anrufkonfiguration](/de/guides/dynamic-call-config).

<Note>
  Abonnierte [Webhook-Endpunkte](/de/webhooks/endpoints) erhalten ebenfalls
  `telephony.incoming` / `web.incoming` — für **jeden** eingehenden Anruf
  und jede Websitzung, unabhängig davon, ob ein Agent konfiguriert ist —
  diese Übermittlungen sind jedoch Fire-and-Forget-Benachrichtigungen mit einer
  `event_id` und niemals blockierend. Nur der Legacy-Single-URL-Webhook enthält
  den Konfigurationsaustausch auf dieser Seite. Die Benachrichtigungsformate der Endpunkte finden Sie im
  [Ereigniskatalog](/de/webhooks/events).
</Note>

## Zeitüberschreitungen und Wiederholungsversuche

Antworten Sie schnell: Der Anrufer wartet in der Leitung, während Sie entscheiden.

Für einen Telefonanruf (`telephony.incoming`):

- Der erste Versuch kann bis zu **10 Sekunden** dauern.
- Wenn er eine Zeitüberschreitung hat, keine Verbindung herstellen kann oder eine
  Antwort mit `5xx`, `408` oder `429` erhält, versucht ThunderPhone es noch einmal. Für den Wiederholungsversuch steht der Rest eines
  **13-Sekunden**-Budgets für den gesamten Austausch zur Verfügung: etwa 3 Sekunden nach
  einem ersten Versuch mit Zeitüberschreitung, nahezu alle 13 nach einem schnellen Fehler.
  Es erfolgt kein Wiederholungsversuch, wenn weniger als 2 Sekunden übrig wären.
- Jede andere `4xx`-Antwort, eine Antwort über dem
  [Größenlimit](#response-size-limit) oder eine `2xx`-Antwort, deren Body keine
  gültige Konfiguration enthält, ist endgültig und wird nicht wiederholt.
- Beide Versuche senden denselben Body, denselben Header `X-ThunderPhone-Signature`
  und denselben Header `X-ThunderPhone-Idempotency-Key`
  (`telephony-incoming-<call_id>`), sodass ein Handler mit Seiteneffekten
  den Wiederholungsversuch erkennen kann.

Es gibt keinen Fallback-Agenten. Wenn keine nutzbare Konfiguration eintrifft, wird der
Anruf sofort beendet, statt den Anrufer in einer stillen Leitung warten zu lassen. Er erscheint im Anrufverlauf als fehlgeschlagener, nicht abgerechneter Anruf mit
`end_reason` `inbound_config_webhook_failed`, und das übliche
[`telephony.complete`](/de/webhooks/call-complete)-Ereignis wird gesendet.

Eine Widget-Sitzung (`web.incoming`) erhält einen einzigen Versuch von bis zu 10
Sekunden. Wenn dieser fehlschlägt, schlägt die Sitzungsanfrage mit `502` oder `422` fehl.

<Warning>
  **Webhook-konfigurierte Anrufe enthalten keine ThunderPhone-Einwilligungsansage.**
  Über diesen Austausch konfigurierte Anrufe umgehen die Ansage auf Agentenebene
  zu Beginn des Anrufs und sind ausdrücklich vom Framework für Einwilligungsansagen von
  ThunderPhone ausgeschlossen (Nutzungsbedingungen, Abschnitt „Aufzeichnung und Einwilligung“).
  Ihre Organisation ist allein verantwortlich für alle Hinweise und Einwilligungen zur Aufzeichnung,
  Überwachung, KI-Beteiligung und Anruferidentifizierung, die für diese Anrufe erforderlich sind —
  die Anrufe können weiterhin aufgezeichnet, transkribiert, analysiert und durch
  KI verarbeitet werden. Integrieren Sie die erforderlichen Hinweise in Ihren eigenen Anruffluss,
  bevor Sie diesen Pfad aktivieren.
</Warning>

## Anfrage-Payload

Für Telefonanrufe (`telephony.incoming`):

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

| Feld | Typ | Beschreibung |
|-------|------|-------------|
| `call_id` | integer | Anruf-ID — über alle Ereignisse für diesen Anruf hinweg stabil |
| `from_number` | string | E.164-Nummer des Anrufers |
| `to_number` | string | E.164-Zielnummer (eine Ihrer ThunderPhone-Nummern) |

Bei Web-Widget-Sitzungen (`web.incoming`) identifiziert `data` statt
Telefonnummern die einbettende Seite:

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

| Feld | Typ | Beschreibung |
|-------|------|-------------|
| `call_id` | Ganzzahl | Anruf-ID |
| `origin_domain` | Zeichenfolge | Der Ursprung der Seite, die das Widget hostet, wie vom Browser übermittelt. Ein Nicht-Browser-Client kann einen beliebigen Wert senden. Behandeln Sie ihn daher nicht als Nachweis dafür, woher die Anfrage stammt |
| `publishable_key_prefix` | Zeichenfolge | Erste Zeichen des veröffentlichbaren Schlüssels, der die Sitzung geöffnet hat |
| `language`, `primary_language` | Zeichenfolge | Vorhanden, wenn die Widget-Sitzung eine Sprachüberschreibung angefordert hat |
| `voice` | Zeichenfolge | Vorhanden, wenn die Widget-Sitzung eine Stimmenüberschreibung angefordert hat |
| `website_context` | Zeichenfolge | Vorhanden, wenn das Widget seitenbezogenen Kontext pro Sitzung übergeben hat |

Die Überschreibungsfelder (`language`, `voice`, `website_context` und alle
Browser-`variables`) werden nur übermittelt, wenn der veröffentlichbare Schlüssel sie in
seinen [Browser-Überschreibungen](/de/guides/embed-web-widget) zulässt. Eine
Sitzungsanfrage, die eine Überschreibung enthält, die der Schlüssel nicht zulässt, wird abgelehnt, bevor
dieser Webhook aufgerufen wird.

<Note>
  Widgets im Webhook-Modus übermitteln diese Anfrage an die eigene
  `webhook_url` des veröffentlichbaren Schlüssels, sofern eine festgelegt ist, und greifen andernfalls auf die
  Webhook-URL der Organisation zurück. In beiden Fällen wird sie mit dem Webhook-`secret` der Organisation signiert.
</Note>

---

## Antwortschema

Geben Sie ein JSON-Objekt zurück, das die Agentenkonfiguration für diesen Anruf beschreibt.
`prompt` und `voice` sind erforderlich; alles andere ist optional.

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

| Feld | Typ | Erforderlich | Beschreibung |
|-------|------|----------|-------------|
| `prompt` | Zeichenfolge | ja | System-Prompt, der den Agenten steuert |
| `voice` | Zeichenfolge | ja | Sprach-ID von [`GET /v1/voices`](/api-reference/agents#voices), z. B. `john`. `voice_name` wird als Alias akzeptiert. Unbekannte Stimmen bestehen die Validierung nicht und lehnen den Anruf ab |
| `product` | Zeichenfolge | nein | Standardmäßig `spark`. Zulässig: `spark`, `bolt`, `storm-base`, `storm-base-with-ack`, `storm-extra`, `storm-extra-with-ack` |
| `thinking_level` | Zeichenfolge | nein | `minimal`, `base` (Standard) oder `extra`. Für Storm-Produkte überschrieben: `storm-extra*` erzwingt `extra`, andere `storm-*` erzwingen `base` |
| `audio_context_mode` | Zeichenfolge | nein | `full` (Standard) oder `reduced` |
| `watchdog_enabled` | boolesch | nein | Überwachung für diesen Anruf aktivieren. Standardmäßig `false` |
| `additional_audio_context` | boolesch \| null | nein | Die letzten paar Gesprächszüge des Anruferaudios statt nur des neuesten Gesprächszugs einbeziehen, um Korrekturen und die Erfassung von Daten mit vielen Buchstaben oder Zahlen bei geringfügig höherer Latenz und Kosten zu verbessern. Für eingehende Sitzungen standardmäßig aktiviert und für ausgehende Telefonanrufe deaktiviert; `null` behält den Standard bei |
| `storm_feedback_mode` | Zeichenfolge | nein | `none`, `acknowledgement` (Standard) oder `tick` |
| `language` | Zeichenfolge | nein | Kurzform für `primary_language` |
| `primary_language` | Zeichenfolge | nein | Sprachcode, normalisiert (Standard `en`). Nicht auflösbare Codes lehnen den Anruf ab |
| `has_additional_languages` | boolesch | nein | Standardmäßig `false` |
| `additional_languages` | Array von Zeichenfolgen | nein | Zusätzliche Sprachen, zu denen der Agent wechseln kann |
| `native_voice_switching` | boolesch | nein | Standardmäßig `false`. Wenn der Anruf zu einer anderen Sprache wechselt, wird zu einer in dieser Sprache heimischen Stimme gewechselt, die nach Geschlecht übereinstimmt, statt die konfigurierte Stimme beizubehalten |
| `language_switching` | Zeichenfolge \| null | nein | `ask_first` (Standard) oder `follow_caller`; `null` entspricht `ask_first`. Legt fest, wie ein mehrsprachiger Agent damit umgeht, wenn die andere Person eine andere seiner Sprachen spricht. `ask_first` wechselt sofort, wenn die Person nach der Sprache fragt oder einen vollständigen Satz darin sagt, und fragt nach etwas Kurzem wie einem „ja“, ob darin fortgefahren werden soll. `follow_caller` wechselt zu jeder konfigurierten Sprache, die die Person spricht, selbst bei einem einzelnen Wort. Gilt nur, wenn `additional_languages` festgelegt ist |
| `background_track` | Zeichenfolge \| null | nein | ID für Hintergrundaudio oder `null` |
| `acknowledgement_prompt_mode` | Zeichenfolge | nein | `auto` (Standard) oder `manual` (Storm-with-ack-Produkte) |
| `acknowledgement_prompt` | Zeichenfolge | nein | Wird verwendet, wenn `acknowledgement_prompt_mode="manual"` |
| `silence_interval_seconds` | Ganzzahl \| null | nein | 5–120. Sekunden der Stille des Anrufers vor einer Nachfrage |
| `silence_max_checkins` | Ganzzahl \| null | nein | 1–10 |
| `silence_checkins_enabled` | boolesch | nein | Standardmäßig `true` |
| `connect_tone_enabled` | boolesch | nein | Standardmäßig `false` |
| `pronunciation_dicts` | boolesch \| null | nein | Standardmäßig `false`; `null` entspricht `false`. Wenn `true`, verwendet die Cartesia-Sprachausgabe des Anrufs das Aussprachewörterbuch von ThunderPhone für die Sprache des Anrufs, sofern eines vorhanden ist. Funktioniert für Telefon- und Widget-Anrufe. Gilt nur für die eigenen Cartesia-Stimmen von ThunderPhone, nicht für Stimmen mit Ihrem eigenen Cartesia-API-Schlüssel. Wird nicht mit `agent_id` verwendet, da dort die Einstellungen des gespeicherten Agenten gelten |
| `voicemail_action` | Zeichenfolge | nein | `prompt` (Standard), `hangup` oder `message` |
| `voicemail_message` | Zeichenfolge | nein | Wird verwendet, wenn `voicemail_action="message"` |
| `agent_name` | Zeichenfolge | nein | Anzeigename, der an Dashboards und das Widget gemeldet wird |
| `org_name` | Zeichenfolge | nein | Anzeigename der Organisation für die Persona des Agenten |
| `tools` | Array | nein | Schemas für Inline-Funktionstools (siehe [Funktionstools](/de/tools/overview)) |
| `transfer_destinations` | Array von Objekten \| null | nein | Bis zu 50 Einträge `{name, number}`, an die dieser Anruf weitergeleitet werden kann, validiert wie die [Weiterleitungsnummern](/api-reference/agents#transfer-numbers) eines Agenten. Der Agent leitet nach Namen und nur an diese weiter; `[]` bedeutet keine Weiterleitungen. Lassen Sie das Feld weg oder senden Sie `null`, damit der Agent an eine selbst gewählte Nummer weiterleiten kann |
| `timezone` | Zeichenfolge | nein | IANA-Zeitzone für diesen Anruf, z. B. `America/Chicago`. Der Agent nennt Datum und Uhrzeit in dieser Zeitzone, die Vorrang vor der eigenen Zeitzone des Agenten hat. Auch neben `agent_id` zulässig. Werte, die keine IANA-Zeitzonen sind, werden ignoriert. Siehe [Zeitzone pro Anruf festlegen](/de/guides/dynamic-call-config) |
| `call_id` | Ganzzahl | nein | Optionales Echo der Call-ID der Anfrage; wird ignoriert |

<Note>
  Unbekannte Schlüssel der obersten Ebene werden stillschweigend **ignoriert** — ein Feldname mit Tippfehler
  lehnt die Konfiguration nicht ab, er wird lediglich nicht angewendet. Sprechreihenfolge
  und `max_hold_seconds` werden hier nicht akzeptiert; sie können nur
  für den [Agenten](/api-reference/agents) selbst konfiguriert werden.
</Note>

Da `prompt` und `voice` erforderlich sind, lehnt die Rückgabe von `{}` oder einer
Antwort, die die Validierung nicht besteht, den Anruf ab: Ein Telefonanruf wird
wie in [Zeitüberschreitungen und Wiederholungsversuche](#timeouts-and-retries) beschrieben beendet, und eine
Widget-Sitzungsanfrage schlägt mit `422` fehl. Für diesen Pfad gibt es keinen
Fallback auf einen statischen Agenten (einer Nummer oder einem Schlüssel im Webhook-Modus ist kein
Agent zugewiesen).

---

## Begrenzung der Antwortgröße

<Warning>
  Konfigurationsantworten sind auf **5 MiB** begrenzt. Wenn ein Handler
  eine größere Antwort zurückgibt, auch mit einem `2xx`-Status,
  meldet ThunderPhone, dass die Antwort das Limit überschritten hat,
  und lehnt den Anruf oder die Widget-Sitzung ab. Beschränken Sie die Antwort auf die für
  den Anrufaufbau erforderlichen Felder; stellen Sie große Daten über Funktionswerkzeuge oder
  einen anderen Dienst bereit, statt sie in die Konfiguration einzubetten.
</Warning>

---

## Beispiel-Handler

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

---

## Antwort mit Funktionswerkzeugen

Fügen Sie Werkzeuge hinzu, damit die KI Ihre APIs während des Gesprächs aufrufen kann:

```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>
  Anfragen an Werkzeug-Endpunkte werden mit demselben **Webhook-Secret
  der Organisation** signiert, das diesen Austausch signiert hat. Siehe
  [Funktionswerkzeuge](/de/tools/overview) für die genaue Struktur und das signierte
  Anfrageformat.
</Tip>

---

## Produktstufen auf einen Blick

| Produkt | Latenz | Schlussfolgern | Bestätigung |
|---------|---------|----------------|-------------|
| `spark` | Niedrigste | Einfach | — |
| `bolt` | Niedrig | Verbessert | — |
| `storm-base` | Mittel | Stark | — |
| `storm-base-with-ack` | Mittel | Stark | Automatischer Fülltext während des Nachdenkens |
| `storm-extra` | Höher | Tiefgehend | — |
| `storm-extra-with-ack` | Höher | Tiefgehend | Automatischer Fülltext während des Nachdenkens |

---

## Verwandt

<CardGroup cols={2}>
  <Card title="telephony.complete / web.complete" icon="phone-slash" href="/de/webhooks/call-complete">
    Das nicht blockierende Ereignis am Ende eines Anrufs.
  </Card>
  <Card title="Funktions-Tools" icon="screwdriver-wrench" href="/de/tools/overview">
    Vollständiges JSON-Schema für `tools[]` und den Vertrag für signierte Endpunkte.
  </Card>
  <Card title="Webhook-Endpunkte" icon="bolt" href="/de/webhooks/endpoints">
    Mehrere URLs für `telephony.incoming` / `web.incoming` abonnieren.
  </Card>
  <Card title="Dynamische Anrufkonfiguration" icon="wand-magic-sparkles" href="/de/guides/dynamic-call-config">
    Muster für anruferspezifische Prompts, Tools und A/B-Tests.
  </Card>
</CardGroup>
