الأساسيات
كيف تعمل الـ Webhooks
نرسل لخادمك طلباً موقّعاً عند كل تغيير مهم في عملية التحقق، فلا تحتاج للاستعلام المتكرر.
الإعداد
- من لوحة المشروع ← Webhooks أضف عنوان خادمك. يجب أن يكون
https. - اختر الأحداث التي تريدها، أو اتركها فارغة لتستقبل كل الأحداث.
- انسخ سر التوقيع (
whsec_…). يظهر مرة واحدة عند الإنشاء.
يمكن إضافة حتى 10 عناوين لكل مشروع، وسجل الإرسال لكل عنوان ظاهر في نفس الصفحة.
الأحداث
| الحدث | متى يُرسل |
|---|---|
verification.sent | وصل الرمز لقناة (أو في وضع الاختبار عند الإنشاء) |
verification.verified | فحص ناجح للرمز |
verification.failed | فشلت كل القنوات أو استُنفدت المحاولات. السبب في data.reason |
verification.expired | انتهت الصلاحية دون تحقق |
قيم reason في verification.failed: all_channels_failed، insufficient_balance، spend_cap_reached (توقف الإرسال في منتصف السلسلة)، وmax_attempts.
شكل الطلب
{
"id": "evt_9f0c2d7a41b84e6f9c3a5b7d1e2f4a6c",
"type": "verification.verified",
"created_at": "2026-10-28T10:01:12.000Z",
"data": {
"id": "5d3c1f0e-7a1b-4c55-9a7e-2f1d6b0c9e11",
"object": "verification",
"mode": "live",
"phone": "+9647701234567",
"status": "verified",
"channel": "telegram",
"channels": [
"whatsapp",
"telegram",
"sms"
],
"attempts": 1,
"max_attempts": 5,
"attempts_remaining": 4,
"locale": "ar",
"metadata": {
"user_id": 8841
},
"expires_at": "2026-10-28T10:05:00.000Z",
"verified_at": "2026-10-28T10:01:12.000Z",
"created_at": "2026-10-28T10:00:00.000Z"
}
}الحقول
idstringإلزاميمعرّف الحدث (
evt_…). استخدمه لتجاهل المكرر.typestringإلزامينوع الحدث.
created_atstringإلزاميوقت وقوع الحدث.
dataobjectإلزاميكائن
verificationكما في حالة عملية التحقق، بدونmessagesوprice_milli. يحملmetadataالخاصة بك، وreasonفي أحداث الفشل.
الترويسات
| الترويسة | القيمة |
|---|---|
Content-Type | application/json |
User-Agent | Watsy-Webhooks/1.0 |
Watsy-Event | نوع الحدث |
Watsy-Delivery | معرّف هذا الإرسال (يبقى نفسه عند إعادة المحاولة) |
Watsy-Signature | t=<unix seconds>,v1=<hex> |
التحقق من التوقيع
v1 هو HMAC-SHA256 بالسر الخاص بك على النص "<t>.<الجسم الخام>" بصيغة hex. احسبه على البايتات الخام قبل تحليل JSON، وقارن بطريقة ثابتة الوقت، وارفض أي t أقدم أو أحدث من 5 دقائق.
import crypto from "node:crypto";
import express from "express";
const app = express();
const SECRET = process.env.WATSY_WEBHOOK_SECRET; // whsec_…
// Keep the raw body: the signature covers the exact bytes we sent.
app.post("/watsy/webhook", express.raw({ type: "application/json" }), (req, res) => {
const raw = req.body.toString("utf8");
const header = req.get("Watsy-Signature") ?? "";
const parts = Object.fromEntries(
header.split(",").map((kv) => {
const i = kv.indexOf("=");
return [kv.slice(0, i).trim(), kv.slice(i + 1).trim()];
}),
);
const t = Number(parts.t);
if (!Number.isInteger(t) || Math.abs(Date.now() / 1000 - t) > 300) return res.sendStatus(400);
const expected = crypto.createHmac("sha256", SECRET).update(`${t}.${raw}`).digest();
const given = Buffer.from(parts.v1 ?? "", "hex");
if (given.length !== expected.length || !crypto.timingSafeEqual(given, expected)) {
return res.sendStatus(400);
}
const event = JSON.parse(raw);
// Deduplicate by event.id, then handle event.type / event.data.
res.sendStatus(200);
});الاستلام وإعادة المحاولة
- أرجع أي رد
2xxخلال 5 ثوانٍ. عالج الحدث بعد الرد إذا كان يحتاج وقتاً. - عند الفشل نعيد بعد 1 دقيقة، ثم 5 دقائق، ثم 30 دقيقة، ثم ساعتين، ثم 6 ساعات. بعد 6 محاولات يُعلَّم الإرسال
failed. - لا نتبع التحويلات (redirects): الرد
3xxيُعتبر فشلاً. - قد يصل الحدث نفسه أكثر من مرة، وقد تصل الأحداث بغير ترتيبها. اعتمد على
idوdata.status.
العنوان يجب أن يكون عاماً: لا نرسل إلى localhost أو عناوين الشبكات الخاصة في الإنتاج.