---
title: "הגדרה דינמית לכל שיחה"
description: "בחרו את הסוכן שעונה — או עדכנו את הפרומפט וההגדרות שלו — בנפרד עבור כל שיחה נכנסת, על בסיס לוגיקה מותאמת אישית ב-webhook שבשליטתכם."
---

כברירת מחדל, לכל מספר טלפון ולכל מפתח לפרסום מוקצה סוכן סטטי.
כאשר נדרשת התאמה אישית **לכל מתקשר** או **לכל מבקר**
— ניתוב VIP, הקשר של משתמש מחובר, בדיקות הנחיה מסוג A/B — עברו
למצב וובהוק ותנו לשרת שלכם להחליט.

## איך זה עובד

1. אתם נרשמים לאירוע [`telephony.incoming`](/he/webhooks/events)
   (טלפון) או [`web.incoming`](/he/webhooks/events) (ווידג'ט).
   שניהם וובהוקים **חוסמים**: ThunderPhone ממתינה לתשובתכם לפני
   המשך השיחה, עד 13 שניות עבור שיחת טלפון
   (כולל ניסיון חוזר אחד) ו-10 שניות עבור סשן וידג'ט.
2. ThunderPhone שולחת לכם `{call_id, from_number, to_number}` (סשני
   וידג'ט כוללים שדות ייעודיים לווידג'ט במקום מספרים — ראו את
   [סכמת הבקשה](/he/webhooks/call-incoming)).
3. השרת שלכם מחזיר תצורת סוכן (הנחיה, קול,
   מוצר, כלים). ThunderPhone משתמשת בתצורה זו עבור השיחה.
4. אין סוכן גיבוי. אם אתם מחזירים `{}` או תצורה
   לא תקפה, או שהבקשה עדיין נכשלת לאחר
   [הניסיון החוזר](/he/webhooks/call-incoming) שלה, שיחת הטלפון
   מסתיימת מיד ונרשמת עם `end_reason`
   `inbound_config_webhook_failed`; בקשת סשן וידג'ט נכשלת.

<Note>
  הפעולה זהה עבור שיחות טלפון (`telephony.incoming`) ומפגשי
  וידג'ט (`web.incoming`), בין אם הם נשלחים לנקודת קצה של וובהוק
  או לוובהוק מדור קודם בעל כתובת URL יחידה.
</Note>

<Warning>
  **לא מושמעת הודעת הסכמה של ThunderPhone בשיחות שמוגדרות באמצעות
  וובהוק מוטבע.** נתיב זה עוקף את הודעת תחילת השיחה ברמת הסוכן
  ומוחרג במפורש ממסגרת הודעות ההסכמה של ThunderPhone
  (תנאי השירות, סעיף "הקלטה והסכמה"). הארגון שלכם אחראי לכל
  ההודעות וההסכמות לגבי הקלטה, ניטור, השתתפות בינה מלאכותית
  וזיהוי מתקשרים בשיחות אלה. ספקו אותן בתהליך שלכם — לדוגמה,
  בתסריט הפתיחה של ההנחיה — לפני הפעלת מצב וובהוק מוטבע. תגובת
  וובהוק שמפנה אל `agent_id` שמור משתמשת במדיניות ההקלטה והגילוי
  הרגילה של אותו סוכן.
</Warning>

## 1. הגדירו את יעד הוובהוק

<Tabs>
<Tab title="שיחות טלפון">
עבור מספרי טלפון, הירשמו עם נקודת הקצה שלכם אל `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"]
  }'
```

התשובה כוללת `secret` חד-פעמי — שמרו אותו; תשתמשו בו
לאימות חתימה.
</Tab>
<Tab title="וידג'ט אינטרנט">
עבור מפגשי וידג'ט, צרו מפתח לפרסום ב-`mode="webhook"`
שכתובת נקודת הקצה שלכם כלולה בו:

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

הווידג'ט ישלח בקשת POST לכתובת זו בתחילת כל מפגש.
</Tab>
</Tabs>

## 2. מימוש המטפל

שלושה כללי אצבע:

- **אמתו את החתימה** בכל בקשה (ראו
  [אימות חתימות webhook](/he/guides/verify-webhook-signatures)).
  אל תדלגו על כך בסביבת פיתוח — הגדירו זאת נכון פעם אחת והשתמשו בו מחדש.
- **השיבו במהירות**. הניסיון הראשון של שיחת טלפון נקטע לאחר 10
  שניות, וכל האינטראקציה לאחר 13 שניות, וכל שנייה היא שקט מת עבור
  המתקשרים. בצעו חיפושים במסד הנתונים אם נדרש, אך אל תקראו למודלי LLM במורד הזרם באופן סינכרוני — אם אתם רוצים
  יצירת הנחיה דינמית, חשבו מראש ושמרו במטמון.
- **בצעו חזרה נקייה לברירת מחדל**. בכל מצב בלתי צפוי, השיבו עם
  `prompt` ו-`voice` המוגדרים כברירת מחדל (או עם `agent_id` שמור). החזרת `{}`
  או סטטוס שגיאה מסיימת את השיחה.

<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. סכמת תגובה

גוף התגובה תואם בדיוק את
[סכמת התגובה לשיחה נכנסת](/he/webhooks/call-incoming).
השדות הנפוצים:

| שדה | סוג | תיאור |
|-------|------|-------------|
| `prompt` | מחרוזת (חובה) | הנחיית מערכת עבור הסוכן |
| `voice` | מחרוזת (חובה) | מזהה קול מתוך [`GET /v1/voices`](/api-reference/agents#voices) |
| `product` | מחרוזת | ברירת המחדל היא `spark`. מוצרים ניסיוניים זמינים רק לארגונים ברשימת ההרשאות. |
| `background_track` | מחרוזת \| null | מזהה שמע סביבתי |
| `acknowledgement_prompt_mode` | מחרוזת | `auto` או `manual` (עבור Storm-with-ack בלבד) |
| `acknowledgement_prompt` | מחרוזת | חובה כאשר המצב הוא `manual` |
| `language_switching` | מחרוזת | עבור סוכנים רב-לשוניים, `ask_first` או `follow_caller` |
| `tools` | מערך | סכמות מוטבעות של כלי פונקציות — ראו [כלי פונקציות](/he/tools/overview) |

<Note>
  סדר הדיבור לכל שיחה ו-`max_hold_seconds` אינם זמינים בתגובת
  הוובהוק. הגדירו אותם ב-[סוכן](/api-reference/agents) שאליו אתם מפנים.
</Note>

### שמרו סוכן והעבירו משתנים

החזירו `{"agent_id": 12, "variables": {"name": "Ada"}}` כדי להשתמש בסוכן השמור של אותו ארגון
עם נתונים לכל שיחה. ההנחיה שלו יכולה להכיל `{{name}}` או
`{{name|Friend}}`. משתני וובהוק גוברים על משתנים ברמת הבקשה; ערך null
משתמש בברירת המחדל של מציין המיקום, או בטקסט ריק אם לא סופק ערך. הערכים
הסופיים והשמות שלא נפתרו מופיעים בפרטי השיחה ובוובהוקים של השלמה.
תגובות של סוכן שמור מקבלות רק את `agent_id`, `variables` ו-`timezone`. אם `prompt`
קיים, התגובה משתמשת בתצורה מוטמעת ומתעלמת מ-`agent_id` (כולל
מטא-נתונים מסוג null או שאינם מספרים שלמים); ההנחיה המוטמעת עדיין חייבת להיות תקפה. תגובות של תצורה מוטמעת
יכולות לכלול גם `variables`. תגובות של סוכן שמור משתמשות בפיצול A/B שהופעל עבור הסוכן גם בשיחות טלפון וגם בשיחות וידג'ט, ולאחר מכן מעבדות את המשתנים. עיינו ב-[משתני שיחה](/he/guides/call-variables)
למידע על מגבלות ותמיכה ב-API של הפעלה. תצורת חסימה מגיעה מ-URL מדור קודם של
מספר טלפון או ארגון, או ממפתח וידג'ט במצב וובהוק; אירועים נכנסים של מערכת נקודות הקצה הם התראות בלבד.

### הגדירו את אזור הזמן לכל שיחה

סוכן אחד יכול לענות לאנשי קשר באזורי זמן שונים. הוסיפו `timezone`, שם IANA, לתגובת סוכן שמור או לתגובה מוטבעת, והסוכן יציין את התאריך והשעה ויציע זמנים באזור זה עבור השיחה, לפני אזור הזמן של הסוכן עצמו:

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

שלחו את האזור במקום חותמת זמן ב-`variables`: השעון של הסוכן ממשיך לפעול במהלך השיחה, וזמן שתעבירו יתיישן. ערך שאינו אזור IANA מתעלמים ממנו, והשיחה מתחברת באזור של הסוכן.

## דפוסים

### הקשר של משתמש מחובר

בווידג'טים במצב וובהוק, דף המבקר כבר יודע מי הם
המשתמשים. קראו לוובהוק שלכם עם פרמטר של מחרוזת שאילתה שערכת ה-SDK של הווידג'ט
מעבירה הלאה (`?customer_id=123`), וחפשו את הלקוח בצד השרת.

### השקת הנחיות מסוג A/B

לפני שתממשו זאת בעצמכם, שימו לב של-ThunderPhone יש יכולת מובנית של
[ניסויים](/he/guides/concepts)
(`/dashboard/experiments` ולשונית **A/B** בבונה הסוכנים), אשר
מגדירה גרסאות, מפצלת תעבורה ומשווה תוצאות לכל גרסה —
ללא צורך בוובהוק.

אם בכל זאת אתם זקוקים לשליטה בצד הוובהוק: בצעו גיבוב של `call_id` → דלי;
הגישו הנחיה A עבור `0..49` והנחיה B עבור `50..99`. תעדו בבסיס הנתונים שלכם
את הדלי שבחרתם, ולאחר מכן קשרו אותו לציון של השיחה שהושלמה.

### ניתוב מבוסס זמן

שעות פעילות → סוכן "תמיכה חיה"; מחוץ לשעות הפעילות → סוכן "קבלת הודעה".
בצעו מיתוג פשוט על `new Date().getUTCHours()` במטפל שלכם.

---

## השלבים הבאים

<CardGroup cols={2}>
  <Card title="הפניה לוובהוק של שיחה נכנסת" icon="phone" href="/he/webhooks/call-incoming">
    סכמות מדויקות של בקשה ותגובה, כולל כל מפתח תצורה.
  </Card>
  <Card title="אמתו חתימות וובהוק" icon="shield-check" href="/he/guides/verify-webhook-signatures">
    הגדירו נכון את HMAC פעם אחת; השתמשו בו מחדש בכל מקום.
  </Card>
  <Card title="בנו שילוב כלי" icon="screwdriver-wrench" href="/he/guides/build-tool-integration">
    שלבו ניתוב דינמי עם כלים לכל סוכן.
  </Card>
  <Card title="סמנטיקת מסירה" icon="bolt" href="/he/webhooks/overview">
    ניסיונות חוזרים, סדר, זמני קצובה.
  </Card>
</CardGroup>
