סקירת Webhooks
כיצד ThunderPhone שולח אירועים בזמן אמת, כיצד לאמת חתימות, וכיצד מודלי המסירה המיושן ומבוסס נקודות הקצה משתווים.
ThunderPhone שולחת בקשות HTTP מסוג POST לשרת שלכם כאשר מתרחשים
אירועים במהלך שיחה — שיחה נכנסת מתחילה, שיחה מסתיימת, הרצת דירוג
מושלמת, התראה מופעלת וכן הלאה. קיימים שני מודלי
מסירה:
כתובות URL מרובות, סודות לכל נקודת קצה, מסנני אירועים לכל נקודת קצה,
וניסיונות חוזרים אוטומטיים.
נהלו באמצעות GET/POST/PATCH/DELETE /v1/developer/webhook-endpoints.
כתובת URL אחת לכל ארגון. כולל את אירועי מחזור החיים של השיחה, לרבות
חילופי התצורה החוסמים. מנוהל ב-GET/PUT /v1/webhook.
כל עשרת סוגי האירועים בקטלוג האירועים נמסרים
באמצעות נקודות קצה של webhook. ששת אירועי מחזור החיים של השיחה
(telephony.incoming, telephony.complete, telephony.tool,
web.incoming, web.complete, web.tool) נשלחים גם אל ה-webhook
המדור קודם עם כתובת URL יחידה — אם יש לכם גם כתובת URL מדור קודם וגם
נקודת קצה תואמת, תקבלו את האירוע בשני הנתיבים. ההתנהגות החוסמת
(חילוף התצורה של telephony.incoming / web.incoming
ושליחת כלים במצב webhook)
קיימת באופן בלעדי בנתיב המדור קודם; כל מסירה לנקודת קצה היא התראה
ללא המתנה לתגובה.
פורמט המטען
מסירות לנקודת קצה הן אובייקט JSON עם data, event_id ו-type:
{
"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:
{
"type": "telephony.incoming",
"data": { "call_id": 987654321, "from_number": "+14155550199", "to_number": "+15551234567" }
}ברשת, כל גוף מסודר באופן קנוני — מפתחות ממוינים לפי סדר אלפביתי, ללא רווחים, UTF-8. הדוגמאות המעוצבות במסמכים אלה נועדו לקריאות בלבד.
עיינו בקטלוג האירועים לרשימה המלאה של סוגי האירועים ושדות המטען.
אימות חתימה
כל בקשה כוללת חתימת HMAC-SHA256 על גוף הבקשה הגולמי בכותרת X-ThunderPhone-Signature. מפתח החתימה הוא ה-secret של נקודת הקצה (או ה-secret של ה-webhook ברמת הארגון שלכם עבור מסירות מדור קודם).
שלבים
- קראו את גוף הבקשה הגולמי לפני כל ניתוח.
- חשבו את
hmac_sha256(secret, body).hexdigest(). - השוו בזמן קבוע לכותרת
X-ThunderPhone-Signature.
אנו חותמים בדיוק על הבתים שאנו שולחים, ובתים אלה הם סריאליזציית ה-JSON הקנונית (מפתחות ממוינים, מפרידים קומפקטיים). לכן אימות מול הגוף הגולמי תמיד פועל — ואם המסגרת שלכם מספקת רק JSON מנותח, סריאליזציה מחדש עם מפתחות ממוינים ומפרידים קומפקטיים מפיקה בתים זהים. שתי השיטות מתועדות במדריך האימות.
import hmac
import hashlib
def verify_signature(body: bytes, signature: str, secret: str) -> bool:
expected = hmac.new(
secret.encode("utf-8"),
body,
hashlib.sha256,
).hexdigest()
return hmac.compare_digest(expected, signature or "")
# Example Flask handler
from flask import Flask, request, abort
app = Flask(__name__)
@app.post("/thunderphone-webhook")
def handle():
body = request.get_data()
sig = request.headers.get("X-ThunderPhone-Signature", "")
if not verify_signature(body, sig, WEBHOOK_SECRET):
abort(401)
event = request.get_json()
# dispatch on event["type"] …
return "", 204import crypto from "node:crypto";
import express from "express";
function verifySignature(body, signature, secret) {
const expected = crypto
.createHmac("sha256", secret)
.update(body)
.digest("hex");
if (!signature || expected.length !== signature.length) return false;
return crypto.timingSafeEqual(
Buffer.from(expected),
Buffer.from(signature),
);
}
const app = express();
app.post(
"/thunderphone-webhook",
express.raw({ type: "application/json" }),
(req, res) => {
const sig = req.header("X-ThunderPhone-Signature") || "";
if (!verifySignature(req.body, sig, process.env.WEBHOOK_SECRET)) {
return res.sendStatus(401);
}
const event = JSON.parse(req.body.toString("utf8"));
// dispatch on event.type …
res.sendStatus(204);
},
);סמנטיקת מסירה
סמנטיקה זו חלה על מסירות אל נקודות קצה. הוובהוק הישן עם כתובת URL יחידה מבצע ניסיון סינכרוני יחיד ללא ניסיונות חוזרים.
ניסיונות חוזרים
כל אירוע נשלח מיד פעם אחת. כל תגובת 2xx
מאשרת את המסירה. בכל תוצאה אחרת (שאינה 2xx,
שגיאת חיבור, תפוגת זמן), ננסה שוב לאחר 1 דקה, 5 דקות, 30 דקות, שעתיים, 6 שעות,
12 שעות ו-24 שעות מהניסיון הראשון — 8 ניסיונות לאורך
24 שעות. אם כל הניסיונות נכשלים, המסירה נפסקת ונקודת הקצה
מסומנת כ-status="failing" ב-
נקודות קצה של וובהוקים. החזירו 2xx ברגע
שהמטען התקבל באופן עמיד; עבדו אותו באופן אסינכרוני.
סדר
סדר המסירה הוא לפי מיטב המאמץ. בפועל אנו מוסרים אירועים לפי
סדר הפליטה שלהם, אך ניסיונות חוזרים יכולים לשנות את הסדר במקרה של כישלון.
תמיד הסירו כפילויות ובצעו התאמה לפי call_id / מזהה אובייקט.
כפילויות
המסירה היא לפחות פעם אחת: ניסיון חוזר לאחר תגובה שמעולם לא
ראינו יכול ליצור אירוע כפול. כל ניסיון חוזר נושא את אותו
event_id, לכן שמרו מזהים שעובדו ודלגו על חזרות. event_id הוא
גם משותף בין נקודות קצה — שתי נקודות קצה שמנויות לאותו אירוע
מקבלות את אותו event_id.
תפוגות זמן
למסירות לנקודות קצה יש תפוגת זמן של 30 שניות לכל ניסיון. בנתיב
הישן, בקשות חוסמות שמניעות התנהגות של שיחה חיה — חילופי התצורה של
telephony.incoming / web.incoming —
מגיעות לתפוגת זמן לאחר 10 שניות, אך תגובה איטית מעכבת את
המענה לשיחה, לכן שאפו להשיב בתוך כמה שניות. שיגור כלים במצב וובהוק
שיגור כלים מאפשר 20 שניות
כברירת מחדל, והצהרות כלים יכולות להגדיר timeout ברמה העליונה.
כתובות IP מקור
וובהוקים יוצאים מגיעים מטווח כתובות ה-IP בענן של ThunderPhone. אם חומת האש שלכם דורשת רשימת היתרים, פנו לתמיכה ונשתף את הטווחים העדכניים.
בחירה בין וובהוקים ישנים לוובהוקים מבוססי נקודות קצה
| תכונה | ישן (/v1/webhook) | נקודות קצה (/v1/developer/webhook-endpoints) |
|---|---|---|
| מספר כתובות URL | 1 לכל ארגון | רבות לכל ארגון |
| כיסוי אירועים | telephony.* / web.* בלבד | כל 10 סוגי האירועים |
| מסנן אירועים | — | לכל נקודת קצה |
| ניסיונות חוזרים | ללא | 8 ניסיונות לאורך 24 שעות |
| מעטפה | type + data | type + data + event_id |
| החלפת סוד | מחליפה סוד יחיד | סוד לכל נקודת קצה |
| השבתה ללא מחיקה | — | status=disabled |
| נראות סטטוס | — | active / disabled / failing |
| חילופי תצורה חוסמים | כן (telephony.incoming / web.incoming) | לעולם לא — התראות בלבד |
| מתאים במיוחד ל | תצורת שיחות דינמית | צריכת אירועים בייצור |
אינטגרציות חדשות צריכות לצרוך אירועים דרך וובהוקים מבוססי נקודות קצה. השאירו (או הוסיפו) כתובת URL ישנה רק אם אתם מגדירים שיחות באופן דינמי בזמן המענה או משתמשים בשיגור כלים במצב וובהוק — חילופי בקשה/תגובה אלה פועלים רק בנתיב הישן.