---
title: "Çağrı başına dinamik yapılandırma"
description: "Kontrol ettiğiniz bir webhook'taki özel mantıkla, her gelen çağrı için yanıtlayan yapay zeka ajanını ayrı ayrı seçin veya ajanın istemini ve ayarlarını yeniden yazın."
---

Varsayılan olarak her telefon numarasına ve yayınlanabilir anahtara statik bir ajan atanır. **Arayan bazında** veya **ziyaretçi bazında** özelleştirme gerektiğinde — VIP yönlendirmesi, oturum açmış kullanıcı bağlamı, A/B istem testleri — webhook moduna geçin ve karar vermeyi sunucunuza bırakın.

## Nasıl çalışır

1. [`telephony.incoming`](/tr/webhooks/events)
   (telefon) veya [`web.incoming`](/tr/webhooks/events) (widget)
   etkinliğine abone olun. Her ikisi de **engelleyici** webhook'lardır:
   ThunderPhone, aramaya devam etmeden önce yanıtınızı bekler; telefon
   araması için 13 saniyeye kadar (bir yeniden deneme dahil), widget oturumu
   için ise 10 saniye bekler.
2. ThunderPhone size `{call_id, from_number, to_number}` gönderir (widget
   oturumları numaralar yerine widget'a özgü alanlar içerir — bkz.
   [istek şeması](/tr/webhooks/call-incoming)).
3. Sunucunuz bir ajan yapılandırmasıyla yanıt verir (istem, ses,
   ürün, araçlar). ThunderPhone bu yapılandırmayı arama için kullanır.
4. Yedek ajan yoktur. `{}` veya geçersiz bir yapılandırma döndürürseniz ya da
   istek [yeniden deneme](/tr/webhooks/call-incoming)
   sonrasında da başarısız olursa, telefon araması hemen sonlandırılır ve
   `end_reason` değeri `inbound_config_webhook_failed` olarak kaydedilir;
   widget oturumu isteği başarısız olur.

<Note>
  Webhook uç noktasına veya eski tek URL'li webhook'a teslim edilmesinden
  bağımsız olarak, telefon aramaları (`telephony.incoming`) ve widget
  oturumları (`web.incoming`) için aynı şekilde çalışır.
</Note>

<Warning>
  **Satır içi webhook ile yapılandırılmış
  aramalarda ThunderPhone onay duyurusu oynatılmaz.** Bu yol, ajan düzeyindeki
  arama başlangıcı duyurusunu atlar ve ThunderPhone'un onay duyurusu
  çerçevesi (Hizmet Koşulları, "Kayıt ve onay" bölümü) kapsamı dışında
  açıkça tutulur. Kuruluşunuz, bu aramalardaki tüm kayıt, izleme, yapay zeka
  katılımı ve arayan kimliği bildirimleri ile onaylarından sorumludur.
  Satır içi webhook modunu etkinleştirmeden önce bunları kendi akışınızda —
  örneğin istemin açılış metninde — sağlayın. Kaydedilmiş bir `agent_id`
  değerine başvuran webhook yanıtı, o ajanın normal kayıt ve bildirim
  politikasını kullanır.
</Warning>

## 1. Webhook hedefini yapılandırın

<Tabs>
<Tab title="Telefon aramaları">
Telefon numaraları için uç noktanızı `telephony.incoming` etkinliğine abone edin:

```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"]
  }'
```

Yanıt tek kullanımlık bir `secret` içerir — kaydedin; bunu
imza doğrulaması için kullanacaksınız.
</Tab>
<Tab title="Web widget">
Widget oturumları için uç nokta URL'niz yerleşik olan `mode="webhook"`
modunda bir yayınlanabilir anahtar oluşturun:

```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"]
  }'
```

Widget, her oturum başlangıcında bu URL'ye POST isteği gönderir.
</Tab>
</Tabs>

## 2. İşleyiciyi uygulayın

Üç temel kural:

- Her istekte **imzayı doğrulayın** (bkz.
  [Webhook imzalarını doğrulama](/tr/guides/verify-webhook-signatures)).
  Geliştirme ortamında bunu atlamayın — bir kez doğru uygulayın ve yeniden kullanın.
- **Hızlı yanıt verin**. Bir telefon görüşmesinin ilk denemesi 10
  saniyede, tüm etkileşim ise 13 saniyede kesilir; her saniye arayan için
  sessizlik demektir. Gerekirse veritabanı sorguları yapın, ancak alt sistemlerdeki LLM'leri eşzamanlı çağırmayın — dinamik istem
  oluşturmak istiyorsanız önceden hesaplayın ve önbelleğe alın.
- **Temiz şekilde geri dönüş yapın**. Beklenmeyen herhangi bir durumda,
  varsayılan bir `prompt` ve `voice` (veya kaydedilmiş bir `agent_id`) ile yanıt verin. `{}`
  veya hata durumu döndürmek görüşmeyi sonlandırır.

<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. Yanıt şeması

Yanıt gövdesi,
[gelen çağrı yanıt şemasıyla](/tr/webhooks/call-incoming)
tam olarak eşleşir. Sık kullanılan alanlar:

| Alan | Tür | Açıklama |
|-------|------|-------------|
| `prompt` | dize (zorunlu) | Ajan için sistem istemi |
| `voice` | dize (zorunlu) | [`GET /v1/voices`](/api-reference/agents#voices) içinden ses kimliği |
| `product` | dize | Varsayılan değer `spark` olur. Deneysel ürünler yalnızca izin verilen kuruluşlar için kullanılabilir. |
| `background_track` | dize \| null | Ortam sesi kimliği |
| `acknowledgement_prompt_mode` | dize | `auto` veya `manual` (yalnızca Storm-with-ack) |
| `acknowledgement_prompt` | dize | Mod `manual` olduğunda zorunludur |
| `tools` | dizi | Satır içi işlev aracı şemaları — bkz. [İşlev Araçları](/tr/tools/overview) |

<Note>
  Çağrı başına konuşma sırası ve `max_hold_seconds`, webhook yanıtında
  kullanılamaz. Bunları başvurduğunuz [Ajan](/api-reference/agents)
  üzerinde ayarlayın.
</Note>

### Kaydedilmiş bir ajanı koruyun ve değişken sağlayın

Çağrı başına verilerle kuruluşun kaydedilmiş ajanını kullanmak için `{"agent_id": 12, "variables": {"name": "Ada"}}`
döndürün. İstemi `{{name}}` veya
`{{name|Friend}}` içerebilir. Webhook değişkenleri istek düzeyindeki değişkenlerle birleştirilir; null
yer tutucunun varsayılan değerini veya belirtilmemişse boş metni kullanır. Nihai
değerler ve çözümlenmemiş adlar çağrı ayrıntılarında ve tamamlama webhook'larında görünür.
Kaydedilmiş ajan yanıtları yalnızca `agent_id`, `variables` ve `timezone` kabul eder. `prompt` mevcutsa
yanıt satır içi yapılandırmayı kullanır ve `agent_id` değerini yok sayar (
null veya tam sayı olmayan meta veriler dahil); satır içi istem yine de geçerli olmalıdır. Satır içi
yapılandırma yanıtları `variables` da içerebilir. Kaydedilmiş ajan yanıtları hem telefon hem de widget çağrılarında
ajanın yayına alınmış A/B dağılımını kullanır, ardından değişkenleri işler. Sınırlar ve
oturum API desteği için [çağrı değişkenleri](/tr/guides/call-variables) bölümüne bakın. Engelleyici yapılandırma, eski
telefon numarası/kuruluş URL'sinden veya webhook modundaki bir widget anahtarından gelir; uç nokta sistemine gelen olaylar
yalnızca bildirimdir.

### Her arama için saat dilimini ayarlayın

Tek bir yapay zeka ajanı, farklı saat dilimlerindeki kişiler için yanıt verebilir. Kaydedilmiş bir ajana veya satır içi yanıta IANA adı olan `timezone` ekleyin; ajan bu arama için tarihi ve saati belirtir ve ajanın kendi saat diliminden önce bu saat dilimine göre saat seçenekleri sunar:

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

`variables` içinde zaman damgası yerine saat dilimini gönderin: ajanın saati arama boyunca çalışmaya devam eder ve ilettiğiniz saat güncelliğini yitirir. IANA saat dilimi olmayan bir değer yok sayılır ve arama ajanın saat diliminde bağlanır.

## Kalıplar

### Oturum açmış kullanıcı bağlamı

Webhook modundaki widget'larda ziyaretçinin sayfası, ziyaretçinin kim olduğunu
zaten bilir. Widget SDK'sının ilettiği bir sorgu dizesi parametresiyle
(`?customer_id=123`) webhook'unuzu çağırın ve müşteriyi sunucu tarafında arayın.

### A/B istem yayına alma

Bunu kendiniz geliştirmeden önce, ThunderPhone'un varyantları tanımlayan,
trafiği bölen ve varyant başına sonuçları karşılaştıran yerel bir
[Deneyler](/tr/guides/concepts) özelliği bulunduğunu unutmayın
(`/dashboard/experiments` ve ajan oluşturucunun **A/B** sekmesi) —
webhook gerekmez.

Yine de webhook tarafında kontrol gerekiyorsa: `call_id` değerini karmalayın → gruba atayın;
`0..49` için istem A'yı, `50..99` için istem B'yi sunun. Seçtiğiniz grubu kendi
veritabanınıza kaydedin ve daha sonra tamamlanan çağrının puanıyla ilişkilendirin.

### Zamana dayalı yönlendirme

Mesai saatleri → "canlı destek" ajanı; mesai dışı → "mesaj alın" ajanı.
İşleyicinizde `new Date().getUTCHours()` üzerinde basit bir geçiş uygulayın.

---

## Sonraki adımlar

<CardGroup cols={2}>
  <Card title="Gelen çağrı webhook başvurusu" icon="phone" href="/tr/webhooks/call-incoming">
    Her yapılandırma anahtarı dahil olmak üzere tam istek ve yanıt şemaları.
  </Card>
  <Card title="Webhook imzalarını doğrulayın" icon="shield-check" href="/tr/guides/verify-webhook-signatures">
    HMAC'i bir kez doğru ayarlayın; her yerde yeniden kullanın.
  </Card>
  <Card title="Bir araç entegrasyonu oluşturun" icon="screwdriver-wrench" href="/tr/guides/build-tool-integration">
    Dinamik yönlendirmeyi ajan başına araçlarla birleştirin.
  </Card>
  <Card title="Teslimat semantiği" icon="bolt" href="/tr/webhooks/overview">
    Yeniden denemeler, sıralama, zaman aşımları.
  </Card>
</CardGroup>
