---
title: "סקירת Webhooks"
description: "כיצד ThunderPhone מעביר אירועים בזמן אמת, כיצד לאמת חתימות, וכיצד מודלי המסירה מדור קודם ומבוססי נקודות קצה משתווים."
---

ThunderPhone שולחת בקשות HTTP מסוג `POST` לשרת שלכם כאשר מתרחשים
אירועים במהלך שיחה — שיחה נכנסת מתחילה, שיחה מסתיימת, הרצת דירוג
מסתיימת, התראה מופעלת וכן הלאה. ישנם **שני מודלי מסירה**:

<CardGroup cols={2}>
  <Card title="נקודות קצה של Webhook (מומלץ)" icon="bolt" href="/he/webhooks/endpoints">
    כתובות URL מרובות, סודות לכל נקודת קצה, מסנני אירועים לכל נקודת קצה
    וניסיונות חוזרים אוטומטיים.
    נהלו באמצעות `GET/POST/PATCH/DELETE /v1/developer/webhook-endpoints`.
  </Card>
  <Card title="Webhook מדור קודם עם כתובת URL יחידה" icon="link" href="/api-reference/organizations#legacy-single-url-webhook">
    כתובת URL אחת לכל ארגון. כולל את אירועי מחזור החיים של השיחה, לרבות
    חילופי התצורה **החוסמים**. מנוהל ב-`GET/PUT /v1/webhook`.
  </Card>
</CardGroup>

כל סוגי האירועים המפורטים ב[קטלוג האירועים](/he/webhooks/events) נמסרים
באמצעות נקודות קצה של Webhook. ששת אירועי מחזור החיים של השיחה
(`telephony.incoming`, `telephony.complete`, `telephony.tool`,
`web.incoming`, `web.complete`, `web.tool`) נשלחים **גם** ל-Webhook
מדור קודם עם כתובת URL יחידה — אם יש לכם גם כתובת מדור קודם וגם נקודת
קצה תואמת, תקבלו את האירוע בשני הנתיבים **גם יחד**. ההתנהגות החוסמת
(חילוף התצורה של [`telephony.incoming` / `web.incoming`](/he/webhooks/call-incoming)
ומשלוח [כלים](/he/tools/overview) במצב Webhook) זמינה
אך ורק בנתיב המדור הקודם; כל מסירה לנקודת קצה היא התראה ללא המתנה לתגובה.

## פורמט המטען

מסירות לנקודות קצה הן אובייקט JSON עם `data`, ‏`event_id` ו-`type`:

```json
{
  "data": {
    "call_id": 987654321,
    "from_number": "+14155550199",
    "to_number": "+15551234567"
  },
  "event_id": "3f6b2ad0-1c9e-4a57-9f2b-8f6f0f9d2f11",
  "type": "telephony.incoming"
}
```

`event_id` ייחודי לכל אירוע שנפלט. הוא זהה בין ניסיונות חוזרים
**וגם** בין כל נקודת קצה שמקבלת את האירוע — בצעו הסרת כפילויות לפיו.

ה-Webhook מדור קודם עם כתובת URL יחידה שולח את אותם `type` ו-`data`, אך
**ללא** `event_id`:

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

במסירה עצמה, כל גוף מסודר באופן קנוני — המפתחות ממוינים אלפביתית,
ללא רווחים, ב-UTF-8. הדוגמאות המעוצבות במסמכים אלה נועדו לקריאות בלבד.

עיינו ב[קטלוג האירועים](/he/webhooks/events) לקבלת הרשימה המלאה של סוגי
האירועים ושדות המטען.

## אימות חתימה

כל בקשה נושאת שתי חתימות, שתיהן מסוג HMAC-SHA256 ומבוססות על ה־`secret` של
נקודת הקצה (או על ה־`secret` של ה־webhook ברמת הארגון שלכם עבור
מסירות מדור קודם):

- `X-ThunderPhone-Signature-V2: t=<unix seconds>,v1=<hex>` חותמת על
  חותמת הזמן, השיטה, הנתיב והשאילתה, ועל הגוף הגולמי. אמתו
  חתימה זו, ודחו בקשות שה־`t` שלהן מרוחק ביותר מ־**5 דקות** משעון
  המערכת שלכם, כדי שלא ניתן יהיה להפעיל מחדש בקשה שנלכדה מאוחר יותר. ניסיונות חוזרים
  נחתמים מחדש בעת שליחתם.
- `X-ThunderPhone-Signature` חותמת על הגוף הגולמי בלבד. היא עדיין נשלחת
  ללא שינוי, כדי שמאמתים קיימים ימשיכו לפעול.

### שלבים

1. קראו את הכותרת, את השיטה, את הנתיב ואת השאילתה הגולמיים, ואת
   הגוף הגולמי **לפני כל ניתוח**.
2. חשבו את ערך ה-HMAC-SHA256 ההקסדצימלי של
   `<t>\n<METHOD>\n<path and query>\n<body>` באמצעות הסוד שלכם.
3. דחו את הבקשה אם `t` חורג ביותר מ-300 שניות, או אם התוצאה אינה
   תואמת ל-`v1` בהשוואה בזמן קבוע.

הגופים הם סריאליזציית JSON קנונית (מפתחות ממוינים ומפרידים קומפקטיים), ואנו חותמים בדיוק על הבתים שאנו מעבירים. [מדריך האימות](/he/guides/verify-webhook-signatures) כולל את המחרוזת החתומה המדויקת, דוגמה מלאה, קוד ללא ה-SDK והכותרת הישנה.

<CodeGroup>
```python Python (Flask)
from flask import Flask, abort, request
from thunderphone import verify_signature

app = Flask(__name__)

@app.post("/thunderphone-webhook")
def handle():
    query = request.query_string.decode()
    if not verify_signature(
        request.headers.get("X-ThunderPhone-Signature-V2"),
        secret=WEBHOOK_SECRET,
        method=request.method,
        path=request.path + (f"?{query}" if query else ""),
        body=request.get_data(),  # raw bytes
    ):
        abort(401)
    event = request.get_json()
    # dispatch on event["type"] …
    return "", 204
```

```javascript Node.js (Express)
import express from "express";
import { verifySignature } from "@thunderphone/sdk";

const app = express();
app.post(
  "/thunderphone-webhook",
  express.raw({ type: "application/json" }),
  (req, res) => {
    const ok = verifySignature({
      header: req.header("X-ThunderPhone-Signature-V2"),
      secret: process.env.WEBHOOK_SECRET,
      method: req.method,
      path: req.originalUrl,
      body: req.body,
    });
    if (!ok) return res.sendStatus(401);
    const event = JSON.parse(req.body.toString("utf8"));
    // dispatch on event.type …
    res.sendStatus(204);
  },
);
```
</CodeGroup>

## סמנטיקת מסירה

סמנטיקות אלה חלות על מסירות אל **נקודת קצה**. הוובהוק מדור קודם עם כתובת URL יחידה
מבצע ניסיון סינכרוני יחיד ללא ניסיונות חוזרים, למעט חילוף התצורה של
שיחת הטלפון [`telephony.incoming`](/he/webhooks/call-incoming),
שמתבצע עבורו ניסיון חוזר אחד.

<AccordionGroup>
  <Accordion title="ניסיונות חוזרים">
    כל אירוע נשלח פעם אחת באופן מיידי. כל תגובת `2xx`
    מאשרת את המסירה. בכל תוצאה אחרת (שאינה 2xx,
    שגיאת חיבור, פסק זמן), ננסה שוב לאחר **1 m, 5 m, 30 m, 2 h, 6 h,
    12 h, ו-24 h מהניסיון הראשון** — 8 ניסיונות הפרוסים על פני
    24 שעות. אם כל הניסיונות נכשלים, המסירה נפסקת ונקודת הקצה
    מסומנת `status="failing"` ב-
    [נקודות קצה של וובהוקים](/he/webhooks/endpoints); נשלח אימייל לבעלים
    ולמנהלים של הארגון שלכם ונציג זאת בלוח הבקרה. החזירו `2xx` ברגע
    שהמטען התקבל באופן עמיד; עבדו אותו באופן אסינכרוני.
    שליחות בדיקה מ-API הבדיקה של נקודת הקצה מקבלות ניסיון חוזר אחד, כדקה לאחר
    הניסיון הראשון, ולעולם אינן משנות את הסטטוס של נקודת הקצה.
  </Accordion>

  <Accordion title="סדר">
    סדר המסירה הוא כמיטב המאמץ. בפועל אנו מוסרים אירועים לפי
    סדר הפליטה שלהם, אך ניסיונות חוזרים עלולים לשנות את הסדר במקרה של כשל.
    מנעו תמיד כפילויות ובצעו התאמה לפי `call_id` / מזהה אובייקט.
  </Accordion>

  <Accordion title="כפילויות">
    המסירה היא **לפחות פעם אחת**: ניסיון חוזר לאחר תגובה שלא
    התקבלה אצלנו עלול לשכפל אירוע. כל ניסיון חוזר נושא את אותו
    `event_id`, לכן שמרו מזהים שעובדו ודלגו על חזרות. `event_id` גם
    משותף בין נקודות קצה — שתי נקודות קצה שמנויות לאותו
    אירוע מקבלות את אותו `event_id`.
  </Accordion>

  <Accordion title="פסקי זמן">
    למסירות לנקודת קצה יש פסק זמן של **30 s** לכל ניסיון. בנתיב
    מדור קודם, בקשות חוסמות שמניעות התנהגות של שיחה חיה — חילוף התצורה של
    [`telephony.incoming` / `web.incoming`](/he/webhooks/call-incoming)
    — מוגבלות ל-**10 s** עבור הניסיון הראשון של שיחת טלפון
    ול-**13 s** עבור החילוף כולו, כולל הניסיון החוזר האחד שלו,
    ול-**10 s** עבור סשן וידג'ט. תגובה איטית מעכבת את
    מענה השיחה, לכן שאפו להשיב בתוך כמה שניות. [שיגור כלים](/he/tools/overview) במצב וובהוק מאפשר 20 s
    כברירת מחדל, והצהרות כלים יכולות להגדיר `timeout` ברמה העליונה.
  </Accordion>

  <Accordion title="כתובות IP מקור">
    וובהוקים יוצאים מגיעים מטווח כתובות ה-IP בענן של ThunderPhone.
    אם חומת האש שלכם דורשת רשימת הרשאה, פנו לתמיכה ואנו
    נשתף את הטווחים העדכניים.
  </Accordion>
</AccordionGroup>

## בחירה בין וובהוקים ישנים לוובהוקים מבוססי נקודות קצה

| מאפיין | מדור קודם (`/v1/webhook`) | נקודות קצה (`/v1/developer/webhook-endpoints`) |
|---------|------------------------|----------------------------------------------|
| מספר כתובות URL | 1 לכל ארגון | רבות לכל ארגון |
| כיסוי אירועים | `telephony.*` / `web.*` בלבד | כל 10 סוגי האירועים |
| סינון אירועים | — | לכל נקודת קצה |
| ניסיונות חוזרים | ללא, למעט ניסיון חוזר אחד עבור החלפת תצורת שיחת טלפון | 8 ניסיונות לאורך 24 שעות |
| מעטפה | `type` + `data` | `type` + `data` + `event_id` |
| החלפת סוד | מחליף סוד יחיד | סוד לכל נקודת קצה |
| השבתה ללא מחיקה | `PUT /v1/webhook` עם `{"url": ""}` | `status=disabled` |
| נראות סטטוס | — | `active` / `disabled` / `failing` |
| החלפת תצורה חוסמת | כן ([`telephony.incoming` / `web.incoming`](/he/webhooks/call-incoming)) | לעולם לא — התראות בלבד |
| המתאים ביותר עבור | תצורת שיחות דינמית | צריכת אירועים בייצור |

שילובים חדשים צריכים לצרוך אירועים באמצעות
וובהוקים מבוססי נקודות קצה. השאירו (או הוסיפו) כתובת URL ישנה רק אם אתם מגדירים שיחות
באופן דינמי בעת המענה או משתמשים בשיגור כלים במצב וובהוק — חילופי
בקשה/תגובה אלה פועלים רק בנתיב הישן.

---

## קשורים

<CardGroup cols={2}>
  <Card title="קטלוג אירועים" icon="list" href="/he/webhooks/events">
    כל סוגי האירועים והמטענים שלהם.
  </Card>
  <Card title="נקודות קצה של Webhook" icon="bolt" href="/he/webhooks/endpoints">
    נהלו נקודות קצה מרובות, מסנני אירועים וסודות.
  </Card>
  <Card title="telephony.incoming / web.incoming" icon="phone" href="/he/webhooks/call-incoming">
    הבקשה החוסמת שהשרת שלכם חייב להשיב לה כדי להגדיר שיחות.
  </Card>
  <Card title="telephony.complete / web.complete" icon="phone" href="/he/webhooks/call-complete">
    מטען לאחר שיחה הכולל תמלול, הקלטה ומדדים.
  </Card>
</CardGroup>
