Open in
ওয়েবহুকের সারসংক্ষেপ
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) লিগ্যাসি একক-URL webhook-এও পাঠানো হয় — আপনার কাছে লিগ্যাসি URL এবং মিল থাকা এন্ডপয়েন্ট উভয়ই থাকলে, আপনি উভয় পাথে ইভেন্টটি পাবেন। ব্লকিং আচরণ (telephony.incoming / web.incoming কনফিগারেশন
বিনিময় এবং webhook-মোডের
টুল ডিসপ্যাচ) একচেটিয়াভাবে লিগ্যাসি পাথে থাকে; প্রতিটি এন্ডপয়েন্ট ডেলিভারি ফায়ার-অ্যান্ড-ফরগেট বিজ্ঞপ্তি।
পেলোড ফরম্যাট
এন্ডপয়েন্ট ডেলিভারি হলো 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 প্রতিটি নির্গত ইভেন্টের জন্য অনন্য। এটি পুনঃচেষ্টার ক্ষেত্রেও
এবং ইভেন্ট গ্রহণকারী প্রতিটি এন্ডপয়েন্টজুড়েও একই থাকে — এটিকে ভিত্তি করে ডিডুপ্লিকেট করুন।
লিগ্যাসি একক-URL webhook একই type এবং data পাঠায়, তবে
event_id ছাড়া:
{
"type": "telephony.incoming",
"data": { "call_id": 987654321, "from_number": "+14155550199", "to_number": "+15551234567" }
}নেটওয়ার্কে প্রতিটি বডি ক্যানোনিক্যালভাবে সিরিয়ালাইজ করা হয় — কী বর্ণানুক্রমে সাজানো, কোনো হোয়াইটস্পেস নেই, UTF-8। এই ডকুমেন্টেশনে থাকা সুন্দরভাবে ফরম্যাট করা উদাহরণগুলো শুধু পাঠযোগ্যতার জন্য।
ইভেন্ট টাইপ এবং পেলোড ফিল্ডের সম্পূর্ণ তালিকার জন্য ইভেন্ট ক্যাটালগ দেখুন।
স্বাক্ষর যাচাই
প্রতিটি রিকোয়েস্টে X-ThunderPhone-Signature হেডারে কাঁচা রিকোয়েস্ট
বডি-র ওপর একটি HMAC-SHA256 স্বাক্ষর থাকে। সাইনিং কী হলো এন্ডপয়েন্টের
secret (অথবা লিগ্যাসি ডেলিভারির জন্য আপনার org-স্তরের webhook secret)।
ধাপসমূহ
- কোনো পার্সিংয়ের আগে কাঁচা রিকোয়েস্ট বডি পড়ুন।
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 প্রতিক্রিয়া
ডেলিভারির স্বীকৃতি দেয়। অন্য যেকোনো ফলাফলে (non-2xx,
সংযোগ ত্রুটি, টাইমআউট) আমরা প্রথম প্রচেষ্টার 1 মিনিট, 5 মিনিট, 30 মিনিট, 2 ঘণ্টা, 6 ঘণ্টা,
12 ঘণ্টা এবং 24 ঘণ্টা পরে পুনঃচেষ্টা করি — 24 ঘণ্টা জুড়ে
8টি প্রচেষ্টা। প্রতিটি প্রচেষ্টা ব্যর্থ হলে ডেলিভারি বন্ধ হয়ে যায় এবং এন্ডপয়েন্টকে
ওয়েবহুক এন্ডপয়েন্ট-এ status="failing" হিসেবে চিহ্নিত করা হয়।
পেলোড স্থায়ীভাবে গ্রহণ করা মাত্র 2xx ফেরত দিন; অ্যাসিঙ্ক্রোনাসভাবে প্রক্রিয়া করুন।
ক্রম
ডেলিভারির ক্রম সর্বোত্তম-প্রচেষ্টাভিত্তিক। বাস্তবে ইভেন্ট নির্গত হওয়ার ক্রমেই আমরা
ডেলিভার করি, তবে ব্যর্থতার ক্ষেত্রে পুনঃচেষ্টা ক্রম পরিবর্তন করতে পারে।
সর্বদা call_id / অবজেক্ট id অনুযায়ী ডুপ্লিকেট বাদ দিন এবং মিলিয়ে নিন।
ডুপ্লিকেট
ডেলিভারি অন্তত-একবার: আমরা যে প্রতিক্রিয়া কখনো পাইনি, তার পরের পুনঃচেষ্টা
একটি ইভেন্টের ডুপ্লিকেট তৈরি করতে পারে। প্রতিটি পুনঃচেষ্টায় একই
event_id থাকে, তাই প্রক্রিয়াকৃত id সংরক্ষণ করুন এবং পুনরাবৃত্তি এড়িয়ে যান। event_id
এন্ডপয়েন্টজুড়েও ভাগ করা হয় — একই ইভেন্টে সাবস্ক্রাইব করা দুটি এন্ডপয়েন্ট একই
event_id পায়।
টাইমআউট
এন্ডপয়েন্ট ডেলিভারিতে প্রতিটি প্রচেষ্টার জন্য 30 সেকেন্ড টাইমআউট থাকে। লিগ্যাসি পথে,
লাইভ কলের আচরণ নিয়ন্ত্রণকারী ব্লকিং অনুরোধ —
telephony.incoming / web.incoming
কনফিগারেশন বিনিময় — 10 সেকেন্ড পরে টাইমআউট হয়, তবে ধীর প্রতিক্রিয়া কল গ্রহণে বিলম্ব ঘটায়,
তাই কয়েক সেকেন্ডের মধ্যে উত্তর দেওয়ার লক্ষ্য রাখুন। ওয়েবহুক-মোড টুল ডিসপ্যাচ
ডিফল্টভাবে 20 সেকেন্ড অনুমোদন করে, এবং টুল ডিক্লারেশন শীর্ষ-স্তরের timeout সেট করতে পারে।
উৎস IP
আউটবাউন্ড ওয়েবহুক ThunderPhone-এর ক্লাউড IP রেঞ্জ থেকে আসে। আপনার ফায়ারওয়ালে allowlist প্রয়োজন হলে সাপোর্টের সঙ্গে যোগাযোগ করুন, আমরা বর্তমান রেঞ্জগুলো শেয়ার করব।
লিগ্যাসি ও এন্ডপয়েন্ট-ভিত্তিক ওয়েবহুকের মধ্যে নির্বাচন
| বৈশিষ্ট্য | লিগ্যাসি (/v1/webhook) | এন্ডপয়েন্ট (/v1/developer/webhook-endpoints) |
|---|---|---|
| URL-এর সংখ্যা | প্রতি org-এ 1টি | প্রতি org-এ অনেকগুলো |
| ইভেন্ট কভারেজ | শুধু telephony.* / web.* | সব 10টি ইভেন্টের ধরন |
| ইভেন্ট ফিল্টার | — | প্রতি এন্ডপয়েন্টে |
| পুনঃচেষ্টা | নেই | 24 ঘণ্টায় 8টি প্রচেষ্টা |
| এনভেলপ | type + data | type + data + event_id |
| সিক্রেট রোটেশন | একক সিক্রেট প্রতিস্থাপন করে | প্রতি এন্ডপয়েন্টে সিক্রেট |
| মুছে না ফেলে নিষ্ক্রিয় করা | PUT /v1/webhook সহ {"url": ""} | status=disabled |
| স্ট্যাটাস দৃশ্যমানতা | — | active / disabled / failing |
| ব্লকিং কনফিগারেশন বিনিময় | হ্যাঁ (telephony.incoming / web.incoming) | কখনো নয় — কেবল নোটিফিকেশন |
| এর জন্য সেরা | ডায়নামিক কল কনফিগারেশন | প্রোডাকশনে ইভেন্ট গ্রহণ |
নতুন ইন্টিগ্রেশনগুলোকে এন্ডপয়েন্ট-ভিত্তিক ওয়েবহুকের মাধ্যমে ইভেন্ট গ্রহণ করা উচিত। কেবল তখনই একটি লিগ্যাসি URL রাখুন (অথবা যোগ করুন), যখন আপনি কল গ্রহণের সময় ডায়নামিকভাবে কল কনফিগার করেন বা ওয়েবহুক-মোড টুল ডিসপ্যাচ ব্যবহার করেন — এই অনুরোধ/প্রতিক্রিয়া বিনিময়গুলো কেবল লিগ্যাসি পথেই চলে।
সম্পর্কিত
সব ইভেন্টের ধরন এবং তাদের পেলোড।
একাধিক এন্ডপয়েন্ট, ইভেন্ট ফিল্টার এবং সিক্রেট পরিচালনা করুন।
কল কনফিগার করতে আপনার সার্ভারকে যে ব্লকিং অনুরোধের উত্তর দিতে হবে।
ট্রান্সক্রিপ্ট, রেকর্ডিং এবং মেট্রিকসহ কল-পরবর্তী পেলোড।