انتقل إلى المحتوى

كل تغيير على أرقامك، موقّعًا ومُرسَلًا إليك.

عشرة أحداث تغطي الرسائل والحالات والتفاعلات والمحادثات والمجموعات والأرقام نفسها. يصل كل حدث كطلب POST بصيغة JSON موقّع، وتُعاد محاولته لمدة 24 ساعة حتى تُجيب نقطة النهاية لديك.

POST https://example.com/hooks/relay
Content-Type: application/json
User-Agent: relay-webhooks/1
X-Relay-Event: message.status
X-Relay-Delivery: dlv_01a1cf088c4e7d2a9b5e1c6f3a8d0e72
X-Relay-Timestamp: 1794474850
X-Relay-Signature: v1=…
{
"id": "evt_01a1cf088c4e7d2a9b5e1c6f3a8d0e72",
"event": "message.status",
"created_at": "2026-11-12T09:14:10.502Z",
"data": { "id": "msg_…", "status": "read", … }
}
التوقيع
HMAC-SHA256
الرد خلال
10 ثوانٍ
إعادة المحاولة
24 ساعة، حتى 10 محاولات
التسليم
مرة واحدة على الأقل

عشرة أحداث

صفِّ كل نقطة نهاية حسب الحدث والرقم. المرشّح الفارغ يستقبل كل شيء، بما في ذلك الأحداث والأرقام المضافة لاحقًا.

الرسائل

المحادثات والمجموعات

الأرقام

message.received

وصلت رسالة إلى أحد أرقامك، في محادثة فردية أو في مجموعة.

data هو كائن الرسالة كاملًا، وهو نفسه الذي يُرجعه GET /v1/messages/{id}.

X-Relay-Event:message.received
{
"id": "evt_01a1cf082f8a7c51b6e0d4a39f12c87e",
"event": "message.received",
"created_at": "2026-11-12T09:13:02.410Z",
"data": {
"id": "msg_01a1cf082d307e19a4c3b85f20d6e91a",
"number_id": "num_01a1a1c6c2007a3f9c41e2b7d5a60813",
"chat_id": "chat_01a1a1d0f4a87c128e5d4b9a2f61c37e",
"chat_type": "dm",
"chat_jid": "212612345678@s.whatsapp.net",
"wa_message_id": "3A9F0C21B7E4D85A6C12",
"from_me": false,
"sender": {
"jid": "212612345678@s.whatsapp.net",
"phone": "+212612345678",
"name": "Maison Exemple",
"contact_id": "ct_01a1a1d0f4b07e2c9a143c8f5b2e7d90"
},
"type": "text",
"body": "Perfect, thank you!",
"content": null,
"media": null,
"quoted_message_id": null,
"quoted_wa_message_id": null,
"mentioned_jids": [],
"is_forwarded": false,
"is_view_once": false,
"status": "delivered",
"error_code": null,
"source": "live",
"client_key": null,
"sent_at": "2026-11-12T09:13:02.000Z",
"edited_at": null,
"revoked_at": null
}
}
  • لا يُرسَل webhook.test إلا عبر POST /v1/webhooks/endpoints/{id}/test أو زر «إرسال حدث تجريبي» في لوحة التحكم، ولا يُرسَل أبدًا بسبب تغيير حقيقي.
  • تصف الأحداث النشاط المباشر. الرسائل المستوردة من سجل الرقم عند ربطه، أو المستعادة بعد انقطاع، يمكن قراءتها عبر API لكنها لا تُرسَل كأحداث.

تحقّق من كل طلب

تحقّق من التوقيع على المحتوى الخام قبل تحليله، وارفض كل ما مضى عليه أكثر من خمس دقائق.

الترويسات

الترويسةما تحملهمثال
Content-TypeJSON دائمًا.application/json
User-Agentتعرّف بالجهة المُرسِلة لدينا.relay-webhooks/1
X-Relay-Eventاسم الحدث، مطابق لـ event في المحتوى.message.received
X-Relay-Deliveryمعرّف التسليم كما يظهر في سجل التسليم.dlv_01a1cf082f8a…
X-Relay-Timestampثواني Unix لحظة توقيع هذه المحاولة.1794474782
X-Relay-Signaturev1= متبوعًا بقيمة HMAC-SHA256 بالنظام الست عشري.v1=5f2b…

مخطط v1

signed_payload = X_Relay_Timestamp + "." + raw_body
signature = "v1=" + hex(HMAC_SHA256(secret, signed_payload))
  1. اقرأ المحتوى الخام كبايتات، قبل أي تحليل لـ JSON.
  2. اجمع X-Relay-Timestamp ونقطة والمحتوى الخام.
  3. احسب HMAC-SHA256 بسرّ نقطة النهاية (سلسلة whsec_… كاملة)، وحوّله إلى النظام الست عشري، وأضف البادئة v1=.
  4. قارن النتيجة بـ X-Relay-Signature بزمن ثابت.
  5. ارفض أي طابع زمني يبتعد عن ساعتك أكثر من 300 ثانية (5 دقائق).

يُعرض السرّ (whsec_…) عند إضافة نقطة النهاية وعند كل تدوير. يوقف التدوير السرّ القديم فورًا، فحدّث المستقبِل لديك مباشرة بعده.

POST/hooks/relay
import crypto from "node:crypto";
import express from "express";
const app = express();
const secret = process.env.RELAY_WEBHOOK_SECRET; // whsec_…
app.post("/hooks/relay", express.raw({ type: "application/json" }), (req, res) => {
const timestamp = req.get("X-Relay-Timestamp") ?? "";
const given = req.get("X-Relay-Signature") ?? "";
const expected = "v1=" + crypto
.createHmac("sha256", secret)
.update(`${timestamp}.${req.body}`)
.digest("hex");
const fresh = Math.abs(Date.now() / 1000 - Number(timestamp)) <= 300;
const valid = given.length === expected.length &&
crypto.timingSafeEqual(Buffer.from(given), Buffer.from(expected));
if (!fresh || !valid) return res.sendStatus(401);
const event = JSON.parse(req.body);
res.sendStatus(204);
// Skip event.id if you have handled it, then act on event.event and event.data.
});
app.listen(3000);

التسليم

مرة واحدة على الأقل، بتوقيع جديد في كل محاولة، مع إعادة المحاولة بفواصل متزايدة لمدة 24 ساعة.

أجب بأي رمز 2xx خلال 10 ثوانٍ، ثم أنجز العمل. تُعاد المحاولة عند انتهاء المهلة وأخطاء الشبكة والرموز 408 و429 و5xx، وأي رمز آخر نهائي، ولا تُتبَع عمليات إعادة التوجيه.

كل محاولة ترسل المحتوى نفسه، ولا يتغيّر إلا X-Relay-Timestamp وX-Relay-Signature.

تسليم واحد، محاولة بعد محاولةmessage.statusevt_01a1cf07f1a27b6e9d20c3a85e17f4b9
succeeded
  1. المحاولة 109:12:44
    http_503أجابت نقطة النهاية لديك بالرمز 503.
    X-Relay-Timestamp: 1794474764المحاولة التالية بعد 30 ث
  2. المحاولة 209:13:14
    timeoutلا إجابة خلال 10 ثوانٍ.
    X-Relay-Timestamp: 1794474794المحاولة التالية بعد 2 د
  3. المحاولة 309:15:24
    200أجابت نقطة النهاية لديك بالرمز 200.
    X-Relay-Timestamp: 1794474924سُلِّم
المحتوى نفسه والمعرّف evt_ نفسه في كل محاولة.

جدول إعادة المحاولة

تتفاوت كل فترة انتظار بنسبة تصل إلى 10% زيادةً أو نقصانًا. عندما تقع المحاولة التالية بعد أكثر من 24 ساعة على الحدث، ينتهي التسليم بالفشل.

المحاولةالانتظارتقريبًا بعد الحدث بـ
1فورًا0 ث
230 ث30 ث
32 د2 د 30 ث
410 د12 د 30 ث
530 د42 د 30 ث
61 س1 س 42 د
72 س3 س 42 د
84 س7 س 42 د
96 س13 س 42 د
108 س21 س 42 د

التكرار

التسليم مضمون مرة واحدة على الأقل. يبقى id في المحتوى (evt_…) والترويسة X-Relay-Delivery (dlv_…) كما هما عبر إعادات المحاولة وإعادات الإرسال: احتفظ بالمعرّفات التي عالجتها وتجاهل التكرار. نقطتا نهاية تستقبلان التغيير نفسه تحصل كل منهما على تسليمها الخاص بمعرّفه الخاص.

التعطيل التلقائي

بعد 10 عمليات تسليم متتالية انتهت بالفشل، كل منها بعد 24 ساعة كاملة من المحاولات، تُعطَّل نقطة النهاية ويصلك إشعار في التطبيق. الانقطاع الأقصر لا يؤدي إلى ذلك أبدًا، وأي نجاح يعيد العدّاد إلى الصفر.

أصلح نقطة النهاية، ثم أعد تفعيلها من لوحة التحكم أو عبر enabled: true. لا يُرسَل شيء ما دامت معطّلة: أعد إرسال الأحداث الفائتة من سجل التسليم.

PATCH /v1/webhooks/endpoints/whk_01a1c5e0a9407e8db0c27f4a19d3e65b
Idempotency-Key: 7c2e4a90-1b3d-4f6e-8a5c-2d9b0e1f3a47
{ "enabled": true }

إعادة الإرسال والاختبار

يعرض سجل التسليم، في لوحة التحكم أو عبر GET /v1/webhooks/deliveries، كل محاولة مع إجابة نقطة النهاية لديك. إعادة إرسال تسليم منتهٍ ترسل المحتوى نفسه والمعرّف نفسه مع نافذة جديدة مدتها 24 ساعة، وتُرجع 409 webhook_delivery_pending ما دام التسليم قيد المحاولة.

يرسل الاختبار حدث webhook.test موقّعًا إلى نقطة نهاية واحدة، حتى لو كانت معطّلة.

POST /v1/webhooks/deliveries/dlv_01a1cf07f1a27b6e9d20c3a85e17f4b9/redeliver
Idempotency-Key: 5b8d1f02-9c4e-4a7b-b6d3-0e2f8a1c7d94
HTTP/1.1 202 Accepted

قواعد نقطة النهاية

عنوان https:// على الإنترنت العام. تُرفض العناوين الخاصة وعناوين الاسترجاع والعناوين المحلية للرابط والعناوين الداخلية للعنقود عند الحفظ (422 invalid_webhook_url)، ويُعاد التحقق منها قبل كل إرسال (destination_not_public، دون إعادة محاولة). لا نتصل إلا بالعنوان الذي تحققنا منه، ولا نتبع عمليات إعادة التوجيه.

الترتيب

يخبرك خطاف الويب بأن شيئًا ما تغيّر، لكن ترتيب الوصول غير مضمون.

  • قد تصل الأحداث بغير ترتيبها، خصوصًا بعد إعادة المحاولة. قارن created_at أو أعد قراءة المورد قبل الكتابة فوق حالة أحدث.
  • حالة الرسالة تتقدّم فقط: pending → sent → delivered → read، أو failed. تجاهل أي حالة تعيدها إلى الوراء.
  • يحمل message.reaction وgroup.participants القائمة الحالية كاملة، ويحمل chat.updated وgroup.updated وnumber.status الحالة بعد التغيير. احتفظ بالأحدث حسب created_at.

أرسل إلى نفسك حدثًا تجريبيًا.

أضف نقطة النهاية من الإعدادات › المطوّرون › الويب هوكس، وأرسل حدثًا تجريبيًا، وتابع وصوله في سجل التسليم.