Open in
סקירת 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 יחידה — אם יש לכם גם כתובת מדור קודם וגם נקודת
קצה תואמת, תקבלו את האירוע בשני הנתיבים גם יחד. ההתנהגות החוסמת
(חילוף התצורה של 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 ומבוססות על ה־secret של
נקודת הקצה (או על ה־secret של ה־webhook ברמת הארגון שלכם עבור
מסירות מדור קודם):
X-ThunderPhone-Signature-V2: t=<unix seconds>,v1=<hex>חותמת על חותמת הזמן, השיטה, הנתיב והשאילתה, ועל הגוף הגולמי. אמתו חתימה זו, ודחו בקשות שה־tשלהן מרוחק ביותר מ־5 דקות משעון המערכת שלכם, כדי שלא ניתן יהיה להפעיל מחדש בקשה שנלכדה מאוחר יותר. ניסיונות חוזרים נחתמים מחדש בעת שליחתם.X-ThunderPhone-Signatureחותמת על הגוף הגולמי בלבד. היא עדיין נשלחת ללא שינוי, כדי שמאמתים קיימים ימשיכו לפעול.
שלבים
- קראו את הכותרת, את השיטה, את הנתיב ואת השאילתה הגולמיים, ואת הגוף הגולמי לפני כל ניתוח.
- חשבו את ערך ה-HMAC-SHA256 ההקסדצימלי של
<t>\n<METHOD>\n<path and query>\n<body>באמצעות הסוד שלכם. - דחו את הבקשה אם
tחורג ביותר מ-300 שניות, או אם התוצאה אינה תואמת ל-v1בהשוואה בזמן קבוע.
הגופים הם סריאליזציית JSON קנונית (מפתחות ממוינים ומפרידים קומפקטיים), ואנו חותמים בדיוק על הבתים שאנו מעבירים. מדריך האימות כולל את המחרוזת החתומה המדויקת, דוגמה מלאה, קוד ללא ה-SDK והכותרת הישנה.
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 "", 204import 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);
},
);סמנטיקת מסירה
סמנטיקות אלה חלות על מסירות אל נקודת קצה. הוובהוק מדור קודם עם כתובת URL יחידה
מבצע ניסיון סינכרוני יחיד ללא ניסיונות חוזרים, למעט חילוף התצורה של
שיחת הטלפון telephony.incoming,
שמתבצע עבורו ניסיון חוזר אחד.
ניסיונות חוזרים
כל אירוע נשלח פעם אחת באופן מיידי. כל תגובת 2xx
מאשרת את המסירה. בכל תוצאה אחרת (שאינה 2xx,
שגיאת חיבור, פסק זמן), ננסה שוב לאחר 1 m, 5 m, 30 m, 2 h, 6 h,
12 h, ו-24 h מהניסיון הראשון — 8 ניסיונות הפרוסים על פני
24 שעות. אם כל הניסיונות נכשלים, המסירה נפסקת ונקודת הקצה
מסומנת status="failing" ב-
נקודות קצה של וובהוקים; נשלח אימייל לבעלים
ולמנהלים של הארגון שלכם ונציג זאת בלוח הבקרה. החזירו 2xx ברגע
שהמטען התקבל באופן עמיד; עבדו אותו באופן אסינכרוני.
שליחות בדיקה מ-API הבדיקה של נקודת הקצה מקבלות ניסיון חוזר אחד, כדקה לאחר
הניסיון הראשון, ולעולם אינן משנות את הסטטוס של נקודת הקצה.
סדר
סדר המסירה הוא כמיטב המאמץ. בפועל אנו מוסרים אירועים לפי
סדר הפליטה שלהם, אך ניסיונות חוזרים עלולים לשנות את הסדר במקרה של כשל.
מנעו תמיד כפילויות ובצעו התאמה לפי call_id / מזהה אובייקט.
כפילויות
המסירה היא לפחות פעם אחת: ניסיון חוזר לאחר תגובה שלא
התקבלה אצלנו עלול לשכפל אירוע. כל ניסיון חוזר נושא את אותו
event_id, לכן שמרו מזהים שעובדו ודלגו על חזרות. event_id גם
משותף בין נקודות קצה — שתי נקודות קצה שמנויות לאותו
אירוע מקבלות את אותו event_id.
פסקי זמן
למסירות לנקודת קצה יש פסק זמן של 30 s לכל ניסיון. בנתיב
מדור קודם, בקשות חוסמות שמניעות התנהגות של שיחה חיה — חילוף התצורה של
telephony.incoming / web.incoming
— מוגבלות ל-10 s עבור הניסיון הראשון של שיחת טלפון
ול-13 s עבור החילוף כולו, כולל הניסיון החוזר האחד שלו,
ול-10 s עבור סשן וידג'ט. תגובה איטית מעכבת את
מענה השיחה, לכן שאפו להשיב בתוך כמה שניות. שיגור כלים במצב וובהוק מאפשר 20 s
כברירת מחדל, והצהרות כלים יכולות להגדיר timeout ברמה העליונה.
כתובות IP מקור
וובהוקים יוצאים מגיעים מטווח כתובות ה-IP בענן של ThunderPhone. אם חומת האש שלכם דורשת רשימת הרשאה, פנו לתמיכה ואנו נשתף את הטווחים העדכניים.
בחירה בין וובהוקים ישנים לוובהוקים מבוססי נקודות קצה
| מאפיין | מדור קודם (/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) | לעולם לא — התראות בלבד |
| המתאים ביותר עבור | תצורת שיחות דינמית | צריכת אירועים בייצור |
שילובים חדשים צריכים לצרוך אירועים באמצעות וובהוקים מבוססי נקודות קצה. השאירו (או הוסיפו) כתובת URL ישנה רק אם אתם מגדירים שיחות באופן דינמי בעת המענה או משתמשים בשיגור כלים במצב וובהוק — חילופי בקשה/תגובה אלה פועלים רק בנתיב הישן.