---
title: "Dynamische Konfiguration pro Anruf"
description: "Wählen Sie für jeden eingehenden Anruf separat den antwortenden Agenten aus – oder schreiben Sie dessen Prompt und Einstellungen neu –, gesteuert durch benutzerdefinierte Logik in einem von Ihnen kontrollierten Webhook."
---

Standardmäßig ist jeder Telefonnummer und jeder veröffentlichbare Schlüssel ein fester Agent zugewiesen. Wenn Sie Anpassungen **pro Anrufer** oder **pro Besucher** benötigen — VIP-Weiterleitung, Kontext für angemeldete Nutzer, A/B-Tests für Prompts — wechseln Sie in den Webhook-Modus und lassen Sie Ihren Server entscheiden.

## So funktioniert es

1. Sie abonnieren das Ereignis [`telephony.incoming`](/de/webhooks/events)
   (Telefon) oder [`web.incoming`](/de/webhooks/events) (Widget).
   Beide sind **blockierende** Webhooks: ThunderPhone wartet auf Ihre
   Antwort, bevor der Anruf fortgesetzt wird — bis zu 13 Sekunden bei einem
   Telefonanruf (einschließlich eines Wiederholungsversuchs) und 10 Sekunden bei einer Widget-Sitzung.
2. ThunderPhone sendet Ihnen `{call_id, from_number, to_number}` (Widget-
   Sitzungen enthalten statt Nummern widgetspezifische Felder — siehe das
   [Anfrageschema](/de/webhooks/call-incoming)).
3. Ihr Server antwortet mit einer Agentenkonfiguration (Prompt, Stimme,
   Produkt, Tools). ThunderPhone verwendet diese Konfiguration für den Anruf.
4. Es gibt keinen Fallback-Agenten. Wenn Sie `{}` oder eine ungültige
   Konfiguration zurückgeben oder die Anfrage nach ihrem
   [Wiederholungsversuch](/de/webhooks/call-incoming) weiterhin fehlschlägt, wird der
   Telefonanruf sofort beendet und mit `end_reason`
   `inbound_config_webhook_failed` aufgezeichnet; die Anfrage einer Widget-Sitzung schlägt fehl.

<Note>
  Funktioniert identisch für Telefonanrufe (`telephony.incoming`) und Widget-
  Sitzungen (`web.incoming`), unabhängig davon, ob sie an einen Webhook-Endpunkt
  oder an den älteren Webhook mit einzelner URL übermittelt werden.
</Note>

<Warning>
  **Bei über Inline-Webhooks konfigurierten Anrufen wird keine
  ThunderPhone-Einwilligungsansage wiedergegeben.** Dieser Pfad umgeht die
  Ansage zum Anrufbeginn auf Agentenebene und ist ausdrücklich vom Framework
  für Einwilligungsansagen von ThunderPhone ausgeschlossen (Nutzungsbedingungen,
  Abschnitt „Aufzeichnung und Einwilligung“). Ihre Organisation ist für alle
  Hinweise und Einwilligungen zu Aufzeichnung, Überwachung, KI-Beteiligung und
  Anruferidentifikation bei diesen Anrufen verantwortlich. Stellen Sie diese
  in Ihrem eigenen Ablauf bereit — beispielsweise im Eröffnungsskript des
  Prompts — bevor Sie den Inline-Webhook-Modus aktivieren. Eine Webhook-Antwort,
  die auf eine gespeicherte `agent_id` verweist, verwendet die normale Richtlinie
  dieses Agenten für Aufzeichnung und Offenlegung.
</Warning>

## 1. Webhook-Ziel konfigurieren

<Tabs>
<Tab title="Telefonanrufe">
Abonnieren Sie für Telefonnummern Ihren Endpunkt für `telephony.incoming`:

```bash
curl -X POST https://api.thunderphone.com/v1/developer/webhook-endpoints \
  -H "Authorization: Bearer sk_live_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "label":  "Prod call-incoming",
    "url":    "https://example.com/thunderphone/incoming",
    "events": ["telephony.incoming"]
  }'
```

Die Antwort enthält ein einmaliges `secret` — speichern Sie es; Sie benötigen
es für die Signaturprüfung.
</Tab>
<Tab title="Web-Widget">
Erstellen Sie für Widget-Sitzungen einen veröffentlichbaren Schlüssel mit
`mode="webhook"`, in dem Ihre Endpunkt-URL hinterlegt ist:

```bash
curl -X POST https://api.thunderphone.com/v1/publishable-key \
  -H "Authorization: Bearer sk_live_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name":            "Dynamic widget",
    "mode":            "webhook",
    "webhook_url":     "https://example.com/thunderphone/widget-incoming",
    "allowed_domains": ["example.com"]
  }'
```

Das Widget sendet bei jedem Sitzungsstart eine POST-Anfrage an diese URL.
</Tab>
</Tabs>

## 2. Implementieren Sie den Handler

Drei Faustregeln:

- **Überprüfen Sie die Signatur** bei jeder Anfrage (siehe
  [Webhook-Signaturen überprüfen](/de/guides/verify-webhook-signatures)).
  Überspringen Sie dies nicht in der Entwicklung — richten Sie es einmal korrekt ein und verwenden Sie es wieder.
- **Antworten Sie schnell**. Der erste Versuch eines Anrufs wird nach 10
  Sekunden abgebrochen und der gesamte Austausch nach 13, und jede Sekunde ist
  Stille für den Anrufer. Führen Sie bei Bedarf Datenbankabfragen durch, rufen Sie jedoch keine
  nachgelagerten LLMs synchron auf — wenn Sie dynamische Prompt-
  Generierung benötigen, berechnen Sie sie vorab und cachen Sie sie.
- **Greifen Sie sauber zurück**. Antworten Sie bei jedem unerwarteten Zustand mit einem
  Standard-`prompt` und einer Standard-`voice` (oder einer gespeicherten `agent_id`). Die Rückgabe von `{}`
  oder eines Fehlerstatus beendet den Anruf.

<CodeGroup>
```python FastAPI
import hashlib
import hmac
import json
import os

from fastapi import FastAPI, HTTPException, Request

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

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

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

    event = json.loads(body)
    if event["type"] not in ("telephony.incoming", "web.incoming"):
        return {}  # fall back to default

    caller = event["data"]["from_number"]
    # Cheap DB lookup: is this a known VIP?
    customer = lookup_customer(caller)
    if customer and customer.tier == "vip":
        return {
            "prompt":  f"You are a VIP concierge for {customer.name}. Be proactive…",
            "voice":   "john",
            "product": "storm-base",
        }
    return {}  # default agent handles non-VIPs

def lookup_customer(phone: str):
    # ... your CRM integration ...
    pass
```

```javascript Express
import crypto from "node:crypto";
import express from "express";

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

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

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

    const IMPORTANT_TYPES = new Set([
      "telephony.incoming",
      "web.incoming",
    ]);
    if (!IMPORTANT_TYPES.has(event.type)) return res.json({});

    const customer = await lookupCustomer(event.data.from_number);
    if (customer?.tier === "vip") {
      return res.json({
        prompt:  `You are a VIP concierge for ${customer.name}. Be proactive…`,
        voice:   "john",
        product: "storm-base",
      });
    }
    res.json({}); // fall back to default agent
  },
);
```
</CodeGroup>

## 3. Antwortschema

Der Antworttext entspricht exakt dem
[Antwortschema für eingehende Anrufe](/de/webhooks/call-incoming).
Die häufig verwendeten Felder:

| Feld | Typ | Beschreibung |
|-------|------|-------------|
| `prompt` | string (erforderlich) | System-Prompt für den Agenten |
| `voice` | string (erforderlich) | Sprach-ID von [`GET /v1/voices`](/api-reference/agents#voices) |
| `product` | string | Standardmäßig `spark`. Experimentelle Produkte sind nur für Organisationen auf der Zulassungsliste verfügbar. |
| `background_track` | string \| null | ID der Hintergrund-Audiospur |
| `acknowledgement_prompt_mode` | string | `auto` oder `manual` (nur Storm mit Bestätigung) |
| `acknowledgement_prompt` | string | Erforderlich, wenn der Modus `manual` ist |
| `language_switching` | string | Für mehrsprachige Agenten: `ask_first` oder `follow_caller` |
| `tools` | array | Inline-Schemas für Funktions-Tools — siehe [Funktions-Tools](/de/tools/overview) |

<Note>
  Die Sprachreihenfolge pro Anruf und `max_hold_seconds` sind in der
  Webhook-Antwort nicht verfügbar. Legen Sie sie für den
  referenzierten [Agenten](/api-reference/agents) fest.
</Note>

### Einen gespeicherten Agenten beibehalten und Variablen übergeben

Geben Sie `{"agent_id": 12, "variables": {"name": "Ada"}}` zurück, um den
gespeicherten Agenten dieser Organisation mit daten pro Anruf zu verwenden. Sein Prompt kann `{{name}}` oder
`{{name|Friend}}` enthalten. Webhook-Variablen werden mit Variablen auf Anforderungsebene zusammengeführt; null
verwendet den Standardwert des Platzhalters oder leeren Text, wenn keiner angegeben ist. Die endgültigen
Werte und nicht aufgelösten Namen erscheinen in den Anrufdetails und Abschluss-Webhooks.
Antworten für gespeicherte Agenten akzeptieren nur `agent_id`, `variables` und `timezone`. Wenn `prompt`
vorhanden ist, verwendet die Antwort die Inline-Konfiguration und ignoriert `agent_id` (einschließlich
null- oder nicht ganzzahliger Metadaten); der Inline-Prompt muss weiterhin gültig sein. Antworten mit Inline-
Konfiguration können ebenfalls `variables` enthalten. Antworten für gespeicherte Agenten verwenden die
bereitgestellte A/B-Aufteilung des Agenten sowohl bei Telefon- als auch bei Widget-Anrufen und rendern anschließend Variablen. Siehe [Anrufvariablen](/de/guides/call-variables)
für Limits und Unterstützung der Sitzungs-API. Die blockierende Konfiguration stammt von der älteren
URL für Telefonnummer/Organisation oder einem Widget-Schlüssel im Webhook-Modus; eingehende Ereignisse des Endpunktsystems sind lediglich Benachrichtigungen.

### Zeitzone pro Anruf festlegen

Ein Agent kann Anrufe von Kontakten in unterschiedlichen Zeitzonen beantworten. Fügen Sie `timezone`, einen
IANA-Namen, zu einer gespeicherten Agentenantwort oder einer Inline-Antwort hinzu, und der Agent nennt für diesen Anruf das Datum und die Uhrzeit und bietet Zeiten in dieser Zone an, unabhängig von der eigenen Zeitzone des Agenten:

```json
{"agent_id": 12, "timezone": "America/Chicago", "variables": {"name": "Ada"}}
```

Senden Sie die Zone statt eines Zeitstempels in `variables`: Die Uhr des Agenten läuft während des Anrufs weiter, und eine übergebene Zeit wird veraltet. Ein Wert, der keine IANA-Zone ist, wird ignoriert, und der Anruf wird in der Zeitzone des Agenten verbunden.

## Muster

### Kontext angemeldeter Benutzer

In Widgets im Webhook-Modus weiß die Seite des Besuchers bereits, wer er ist.
Rufen Sie Ihren Webhook mit einem Query-String-Parameter auf, den das Widget-SDK
weiterleitet (`?customer_id=123`), und suchen Sie den Kunden serverseitig.

### A/B-Prompt-Rollout

Bevor Sie dies selbst implementieren, beachten Sie, dass ThunderPhone eine native
Funktion für [Experimente](/de/guides/concepts) bietet
(`/dashboard/experiments` und den Tab **A/B** im Agent-Builder), die Varianten
definiert, Traffic aufteilt und Ergebnisse je Variante vergleicht —
kein Webhook erforderlich.

Falls Sie dennoch eine Steuerung auf Webhook-Seite benötigen: Hashen Sie `call_id`
→ Bucket; liefern Sie Prompt A für `0..49` und Prompt B für `50..99` aus.
Speichern Sie den gewählten Bucket in Ihrer eigenen Datenbank und korrelieren Sie
ihn später mit der Bewertung des abgeschlossenen Anrufs.

### Zeitbasierte Weiterleitung

Geschäftszeiten → Agent für „Live-Support“; außerhalb der Geschäftszeiten → Agent
für „Nachricht aufnehmen“. Einfacher Wechsel basierend auf
`new Date().getUTCHours()` in Ihrem Handler.

---

## Nächste Schritte

<CardGroup cols={2}>
  <Card title="Webhook-Referenz für eingehende Anrufe" icon="phone" href="/de/webhooks/call-incoming">
    Exakte Anfrage- und Antwortschemata, einschließlich aller Konfigurationsschlüssel.
  </Card>
  <Card title="Webhook-Signaturen prüfen" icon="shield-check" href="/de/guides/verify-webhook-signatures">
    Richten Sie HMAC einmal korrekt ein und verwenden Sie es überall wieder.
  </Card>
  <Card title="Eine Tool-Integration erstellen" icon="screwdriver-wrench" href="/de/guides/build-tool-integration">
    Kombinieren Sie dynamische Weiterleitung mit Tools pro Agent.
  </Card>
  <Card title="Zustellsemantik" icon="bolt" href="/de/webhooks/overview">
    Wiederholungen, Reihenfolge, Timeouts.
  </Card>
</CardGroup>
