Open in
Tổng quan về Webhook
Cách ThunderPhone gửi sự kiện theo thời gian thực, cách xác minh chữ ký và so sánh mô hình gửi cũ với mô hình gửi dựa trên endpoint.
ThunderPhone gửi yêu cầu HTTP POST đến máy chủ của bạn khi các sự kiện
xảy ra trong cuộc gọi — cuộc gọi đến bắt đầu, cuộc gọi kết thúc, một lần chấm điểm
hoàn tất, một cảnh báo được kích hoạt, v.v. Có hai mô hình
phân phối:
Nhiều URL, secret riêng cho từng điểm cuối, bộ lọc sự kiện riêng cho từng điểm cuối,
và tự động thử lại.
Quản lý qua GET/POST/PATCH/DELETE /v1/developer/webhook-endpoints.
Một URL cho mỗi tổ chức. Chứa các sự kiện trong vòng đời cuộc gọi, bao gồm cả các
trao đổi cấu hình chặn. Quản lý tại GET/PUT /v1/webhook.
Mọi loại sự kiện được liệt kê trong danh mục sự kiện đều
được gửi qua điểm cuối webhook. Sáu sự kiện trong vòng đời cuộc gọi
(telephony.incoming, telephony.complete, telephony.tool,
web.incoming, web.complete, web.tool) cũng được gửi đến
webhook kế thừa một URL — nếu bạn có cả URL kế thừa và một điểm cuối
khớp, bạn sẽ nhận sự kiện trên cả hai đường dẫn. Hành vi chặn (gồm
trao đổi cấu hình telephony.incoming / web.incoming
và điều phối công cụ ở chế độ webhook)
chỉ có trên đường dẫn kế thừa; mọi lần gửi đến điểm cuối đều là thông báo
gửi đi mà không chờ phản hồi.
Định dạng payload
Các lần gửi đến điểm cuối là một đối tượng JSON có data, event_id và
type:
{
"data": {
"call_id": 987654321,
"from_number": "+14155550199",
"to_number": "+15551234567"
},
"event_id": "3f6b2ad0-1c9e-4a57-9f2b-8f6f0f9d2f11",
"type": "telephony.incoming"
}event_id là duy nhất cho mỗi sự kiện được phát ra. Giá trị này giống hệt giữa các lần thử lại
và giữa mọi điểm cuối nhận sự kiện — hãy dùng giá trị này để loại bỏ trùng lặp.
Webhook kế thừa một URL gửi cùng type và data nhưng
không có event_id:
{
"type": "telephony.incoming",
"data": { "call_id": 987654321, "from_number": "+14155550199", "to_number": "+15551234567" }
}Trên đường truyền, mọi phần thân đều được tuần tự hóa theo chuẩn — các khóa được sắp xếp theo thứ tự bảng chữ cái, không có khoảng trắng, UTF-8. Các ví dụ được định dạng đẹp trong tài liệu này chỉ nhằm mục đích dễ đọc.
Xem Danh mục sự kiện để biết danh sách đầy đủ các loại sự kiện và trường payload.
Xác minh chữ ký
Mỗi yêu cầu đều mang hai chữ ký, cả hai đều dùng HMAC-SHA256 với khóa là secret của
endpoint (hoặc secret webhook cấp tổ chức của bạn đối với các lần gửi cũ):
X-ThunderPhone-Signature-V2: t=<unix seconds>,v1=<hex>ký dấu thời gian, phương thức, đường dẫn và truy vấn, cùng phần thân thô. Xác minh chữ ký này và từ chối các yêu cầu cótchênh lệch quá 5 phút so với đồng hồ của bạn để yêu cầu bị chặn không thể bị phát lại sau đó. Các lần thử lại được ký lại khi gửi.X-ThunderPhone-Signaturechỉ ký phần thân thô. Chữ ký này vẫn được gửi, không thay đổi, để các trình xác minh hiện có tiếp tục hoạt động.
Các bước
- Đọc header, phương thức, đường dẫn và truy vấn thô, cùng phần body thô trước khi thực hiện bất kỳ quá trình phân tích nào.
- Tính HMAC-SHA256 dạng hex của
<t>\n<METHOD>\n<path and query>\n<body>bằng secret của bạn. - Từ chối yêu cầu nếu
tlệch quá 300 giây hoặc nếu kết quả không khớp vớiv1trong phép so sánh thời gian hằng số.
Body là bản tuần tự hóa JSON chuẩn (khóa được sắp xếp, dấu phân cách gọn), và chúng tôi ký chính xác các byte được truyền đi. Hướng dẫn xác minh có chuỗi đã ký chính xác, ví dụ hoàn chỉnh, mã không dùng SDK và header cũ.
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);
},
);Ngữ nghĩa phân phối
Các ngữ nghĩa này áp dụng cho lần gửi đến endpoint. Webhook URL đơn lẻ cũ
là một lần thử đồng bộ duy nhất không có lần thử lại, ngoại trừ
trao đổi cấu hình telephony.incoming
cho cuộc gọi điện thoại, được thử lại một lần.
Thử lại
Mỗi sự kiện được thử ngay lập tức một lần. Mọi phản hồi 2xx
đều xác nhận lần gửi. Với mọi kết quả khác (không phải 2xx,
lỗi kết nối, hết thời gian chờ), chúng tôi thử lại sau 1 phút, 5 phút, 30 phút, 2 giờ, 6 giờ,
12 giờ và 24 giờ kể từ lần thử đầu tiên — 8 lần thử trong
24 giờ. Nếu mọi lần thử đều thất bại, việc gửi sẽ dừng và endpoint
được đánh dấu status="failing" trong
endpoint webhook; chúng tôi gửi email cho
chủ sở hữu và quản trị viên của tổ chức bạn, đồng thời hiển thị thông tin này trên bảng điều khiển. Trả về 2xx ngay khi
payload được chấp nhận và lưu bền vững; xử lý không đồng bộ.
Lần gửi kiểm thử từ API kiểm thử endpoint được thử lại một lần khoảng một phút sau
lần thử đầu tiên và không bao giờ thay đổi trạng thái của endpoint.
Thứ tự
Thứ tự gửi là theo nỗ lực tốt nhất. Trên thực tế, chúng tôi gửi theo thứ tự sự kiện được phát ra, nhưng việc thử lại có thể làm thay đổi thứ tự khi xảy ra lỗi.
Luôn khử trùng lặp và đối soát theo call_id / ID đối tượng.
Bản sao
Việc gửi là ít nhất một lần: một lần thử lại sau phản hồi mà chúng tôi không nhận được có thể tạo bản sao của sự kiện. Mỗi lần thử lại mang cùng event_id, vì vậy hãy lưu các ID đã xử lý và bỏ qua các lần lặp lại. event_id cũng được dùng chung giữa các endpoint — hai endpoint đăng ký cùng một sự kiện sẽ nhận cùng event_id.
Thời gian chờ
Lần gửi đến endpoint có thời gian chờ 30 giây cho mỗi lần thử. Trên
đường dẫn cũ, các yêu cầu chặn chi phối hành vi cuộc gọi trực tiếp — trao đổi
cấu hình telephony.incoming / web.incoming
— bị giới hạn ở 10 giây cho lần thử đầu tiên của một cuộc gọi điện thoại
và 13 giây cho toàn bộ quá trình trao đổi, bao gồm một lần thử lại,
và ở 10 giây cho một phiên widget. Phản hồi chậm sẽ làm chậm
việc nhận cuộc gọi, vì vậy hãy cố gắng phản hồi trong vài giây. Điều phối công cụ ở chế độ webhook
mặc định cho phép 20 giây và khai báo công cụ có thể đặt timeout ở cấp cao nhất.
IP nguồn
Webhook gửi đi xuất phát từ dải IP đám mây của ThunderPhone. Nếu tường lửa của bạn yêu cầu danh sách cho phép, hãy liên hệ bộ phận hỗ trợ và chúng tôi sẽ chia sẻ các dải hiện tại.
Chọn giữa webhook cũ và webhook dựa trên endpoint
| Tính năng | Phiên bản cũ (/v1/webhook) | Endpoint (/v1/developer/webhook-endpoints) |
|---|---|---|
| Số lượng URL | 1 cho mỗi tổ chức | Nhiều cho mỗi tổ chức |
| Phạm vi sự kiện | Chỉ telephony.* / web.* | Cả 10 loại sự kiện |
| Bộ lọc sự kiện | — | Theo từng endpoint |
| Thử lại | Không có, trừ một lần cho quá trình trao đổi cấu hình cuộc gọi điện thoại | 8 lần thử trong 24 giờ |
| Bao bọc | type + data | type + data + event_id |
| Xoay vòng secret | Thay thế secret duy nhất | Secret theo từng endpoint |
| Vô hiệu hóa không cần xóa | PUT /v1/webhook với {"url": ""} | status=disabled |
| Hiển thị trạng thái | — | active / disabled / failing |
| Trao đổi cấu hình chặn | Có (telephony.incoming / web.incoming) | Không bao giờ — chỉ thông báo |
| Phù hợp nhất cho | Cấu hình cuộc gọi động | Tiêu thụ sự kiện trong môi trường production |
Các tích hợp mới nên tiêu thụ sự kiện qua webhook dựa trên endpoint. Chỉ giữ lại (hoặc thêm) URL cũ nếu bạn cấu hình cuộc gọi động tại thời điểm nhận cuộc gọi hoặc sử dụng điều phối công cụ ở chế độ webhook — các trao đổi yêu cầu/phản hồi đó chỉ chạy trên đường dẫn cũ.
Liên quan
Tất cả loại sự kiện và payload của chúng.
Quản lý nhiều điểm cuối, bộ lọc sự kiện và bí mật.
Yêu cầu chặn mà máy chủ của bạn phải phản hồi để cấu hình cuộc gọi.
Payload sau cuộc gọi có bản chép lời, bản ghi âm và chỉ số.