شرح Facebook Messenger Postbacks: التعامل مع نقرات الأزرار في الـ webhooks
13 مايو 2026
·
14 دقيقة قراءة
في هذا الدليل: ما هو الـ postback · تدفّق البيانات · جميع أنواع الأزرار الثلاثة التي تولّد postbacks · مخططات حمولة الـ webhook بدقة · الفخّ الخفي في اشتراك messaging_postbacks · مقارنة postback مقابل quick reply · أنماط تصميم الـ payload · postbacks الإحالة من الإعلانات · نظام التوجيه الإنتاجي · التنسيق الموحّد في SocialHook
ما هو Facebook Messenger postback؟
الـ postback هو حدث webhook يُطلَق عندما ينقر المستخدم على زر مُهيّأ بسلسلة payload داخل Facebook Messenger. الـ payload هو سلسلة نصية تُعرّفها أنت عند إنشاء الزر — فهي تنتقل من خادمك إلى Meta ثم إلى جهاز المستخدم، ثم تعود إلى خادمك عند النقر. وهي الطريقة التي يخبر بها Messenger الـ bot الخاص بك: "اتخذ المستخدم خياراً مقصوداً، وهذا هو الخيار."
على عكس الرسالة النصية (حيث يكتب المستخدم شيئاً غير متوقّع)، يحمل الـ postback بالضبط الـ payload الذي قمت بتهيئته — لا حاجة إلى NLP، ولا مطابقة ضبابية، ولا اكتشاف نوايا. ينقر المستخدم على "تتبّع طلبي" ويستقبل خادمك TRACK_ORDER. حتمي، نظيف، وموثوق.
تُشغّل آلية الـ postback ثلاثة من أهم العناصر التفاعلية في Messenger:
أزرار Postback داخل رسائل القوالب (button template، وعرض الـ generic template carousel)
عناصر القائمة الدائمة (أيقونة الهامبرغر أسفل كل محادثة)
مداخل الإحالة (المستخدمون القادمون عبر روابط m.me أو إعلانات Click-to-Messenger)
تدفّق البيانات الكامل: من النقر إلى حدث الـ webhook
🔘
أنت تُهيّئ
زر بـ payload: "TRACK_ORDER"
👆
المستخدم ينقر
على الزر في واجهة Messenger
📡
Meta تُطلق
POST إلى عنوان webhook الخاص بك
⚡
خادمك
event.postback .payload === "TRACK_ORDER"
التفصيل الحرج في ذلك التدفّق: حدث الـ webhook الذي تُطلقه Meta هو حدث postback — فهو يصل تحت entry.messaging[].postback، وليس تحت entry.messaging[].message. لهذا السبب يُغفله كثير من المطوّرين: يكتبون معالج رسائل ويتساءلون لماذا لا تصل نقرات الأزرار أبداً. راجع قسم الاشتراك أدناه لمعرفة سبب حدوث ذلك وكيفية إصلاحه.
جميع أنواع الأزرار الثلاثة التي تُولّد postbacks
زر Postback
داخل القوالب
يظهر داخل رسائل button template، أو generic template (carousel)، أو media template. يبقى مرئياً في المحادثة بعد النقر عليه. الحد الأقصى 3 أزرار لكل button template، و3 لكل بطاقة generic template. الحد الأقصى لتسمية الزر 20 حرفاً.
يصل الـ payload إلى
event.postback.payload
عنصر القائمة الدائمة
مرئي دائماً
يُضبط عبر Messenger Profile API مرة واحدة لكل صفحة — يظهر كأيقونة قائمة هامبرغر لجميع المستخدمين. يُسمح بهيكل متداخل (مستويان). عناصر القائمة من النوع "postback" تُطلق أحداث postback؛ أما النوع "web_url" فيفتح رابطاً دون إطلاق postback.
يصل الـ payload إلى
event.postback.payload
Quick Reply
يختفي بعد النقر
يظهر كفقاعات قابلة للتمرير أفقياً. يختفي بعد أن ينقر المستخدم على أحدها أو يرسل رسالة أخرى. على الرغم من أنه يُطلق سلسلة "payload"، إلا أن quick replies لا تصل في أحداث postback — بل تصل في أحداث الرسائل. راجع المقارنة أدناه.
يصل الـ payload إلى
event.message.quick_reply.payload
مخططات حمولة الـ webhook بدقّة لكل نوع
زر Postback وعنصر القائمة الدائمة
Postback event — full raw webhook payload
{
"object": "page",
"entry": [{
"id": "PAGE_ID",
"messaging": [{
"sender": { "id": "12345678901234" }, // PSID of user who tapped"recipient": { "id": "PAGE_ID" },
"timestamp": 1747231892100,
"postback": { // ← THIS IS THE POSTBACK OBJECT (not .message)"mid": "m_abc123...", // message ID of the button message"title": "Track My Order",// the button label the user saw and tapped"payload": "TRACK_ORDER"// ← YOUR PAYLOAD — what you configured on the button
}
}]
}]
}
// Extraction: event.postback.payload === "TRACK_ORDER"// event.postback.title === "Track My Order" (the label — useful for logging)// event.sender.id === PSID of the user who tapped
Quick reply (ليس postback — يصل في أحداث الرسائل)
Quick reply event — lives in .message, not .postback
{
"messaging": [{
"sender": { "id": "12345678901234" },
"timestamp": 1747231892100,
"message": { // ← note: .message, not .postback"mid": "m_def456...",
"text": "Yes, please!", // the quick reply title sent as a message text"quick_reply": {
"payload": "CONFIRM_YES"// ← YOUR PAYLOAD — same concept, different location
}
}
}]
}
// Extraction: event.message.quick_reply.payload === "CONFIRM_YES"// event.message.text still contains the quick reply title text// Subscription needed: messages (NOT messaging_postbacks)
الفخّ الخفي في الاشتراك: لماذا لا تصل postbacks الخاصة بك
⚠️
يجب الاشتراك في messaging_postbacks بشكل منفصل
هذا هو السبب رقم 1 الذي يدفع المطوّرين للإبلاغ بأن "أزراري لا تعمل" — لقد اشتركوا في messages لكن ليس في messaging_postbacks. وهذان اشتراكان منفصلان في حقول الـ webhook. الاشتراك في أحدهما لا يشمل الآخر. وبدون messaging_postbacks، لن تستقبل نقطة نهاية POST الخاصة بك حدث postback أبداً، حتى لو كان الزر الخاص بك مُهيّأً بشكل صحيح مع payload.
✓ messages ← الرسائل النصية، المرفقات، quick replies
✓ messaging_postbacks ← نقرات الأزرار، القائمة الدائمة، الإحالات
✗ غير مُضمَّن تلقائياً
Graph API (برمجياً): POST إلى /PAGE_ID/subscribed_apps مع subscribed_fields=messages,messaging_postbacks,message_reads
أزرار Postback مقابل quick replies: متى تستخدم أيّاً منها
التمييز الجوهري في معالج الـ webhook الخاص بك: أحداث postback تصل إلى event.postback؛ بينما حمولات quick reply تصل إلى event.message.quick_reply. يحتاج الموجّه (router) لديك إلى التحقّق من كلا الموقعين.
Node.js — detect and extract all payload types
detectPayload.js
functionextractPayload(event) {
// 1. Standard postback button or persistent menu tapif (event.postback?.payload) {
return {
type: 'POSTBACK',
payload: event.postback.payload,
title: event.postback.title, // button label — useful for logging
};
}
// 2. Quick reply button tap (arrives in message event)if (event.message?.quick_reply?.payload) {
return {
type: 'QUICK_REPLY',
payload: event.message.quick_reply.payload,
title: event.message.text, // quick reply title sent as message text
};
}
// 3. Referral postback (user arrived via m.me link or ad)if (event.postback?.referral || event.referral) {
const ref = event.postback?.referral || event.referral;
return {
type: 'REFERRAL',
payload: event.postback?.payload || null,
ref: ref.ref, // your tracking parameter
source: ref.source, // 'SHORTLINK' | 'ADS' | 'CUSTOMER_CHAT_PLUGIN' | etc
};
}
// 4. Regular text message — no payloadif (event.message?.text) {
return {
type: 'TEXT',
payload: null,
text: event.message.text,
};
}
return { type: 'UNKNOWN', payload: null };
}
أنماط تصميم الـ payload: الجيّد مقابل السيّئ
سلسلة الـ payload مِلْكٌ لك لتصمّمها — فإن Facebook يتعامل معها كسلسلة معتمة. وطريقة تصميمك للـ payloads هي التي تحدّد مدى قابلية الصيانة وقابلية التصحيح في منطق الـ bot الخاص بك عند التوسّع. إليك الأنماط التي تنجح وتلك التي تُسبّب المشاكل:
سهلة التوجيه باستخدام startsWith() أو switch. تخبرك البادئة بالتدفّق، واللاحقة تخبرك بالاختيار. مقروءة في السجلّات. وقصيرة بما يكفي للبقاء تحت حدّ 1,000 حرف بأمان.
استخدم JSON.stringify() على الكائن، ثم JSON.parse() عند الاستقبال. استخدم ذلك فقط عندما تحتاج إلى تمرير سياق لا يمكن استنتاجه من حالة المحادثة. وحافظ على الحجم مُدمَجاً — فكل حرف يُحسَب ضمن حدّ 1,000.
✗ سيّئ — كلمات مفردة مبهمة
payloads متضاربة وغامضة
YES NO NEXT OK BACK
هذه تتضارب عبر التدفّقات. "YES" لماذا؟ في أيّ حالة محادثة؟ إذا كان لديك قالبا أزرار مختلفان يستخدمان كلاهما "YES"، فلن يستطيع الموجّه التمييز بينهما دون فحص حالة المحادثة في كل مرة.
✗ سيّئ — فارغة أو نص مرئي للمستخدم
payloads غير قابلة للقراءة آلياً
"Yes, please!" "Track my order" ""
سلاسل payload الفارغة تجعل Facebook ترفض إعداد الزر. والنص المرئي للمستخدم كـ payload ينكسر عندما تُغيّر تسمية الزر دون تحديث الموجّه. يجب أن تكون الـ payloads معرّفات آلية لا سلاسل عرض.
أضف رقم إصدار إلى payloads القائمة الدائمة. القائمة الدائمة تُضبط عالمياً لجميع مستخدمي صفحتك. فإن أضفت عنصر قائمة يُغيّر سلسلة payload، فسيحصل المستخدمون الحاليون الذين لم يفتحوا Messenger بعد على القائمة الجديدة. استخدم لواحق الإصدار مثل MENU_SUPPORT_V2 حتى يتعامل الموجّه مع payloads القديمة والجديدة معاً خلال فترات الانتقال. لا تُعِدْ استخدام سلسلة payload لإجراء مختلف أبداً.
postbacks الإحالة: تتبّع نقاط الدخول لكل حملة
عندما ينقر مستخدم على رابط m.me يحمل وسيطة ref — مثل m.me/yourpage?ref=EMAIL_CAMPAIGN_MAY — أو يصل عبر إعلان Click-to-Messenger، تُطلق Facebook حدث إحالة. هذه هي الطريقة التي تتتبّع بها أيّ حملة أو إعلان أو نقطة دخول جلبت المستخدم إلى Messenger.
تأتي أحداث الإحالة في شكلين بحسب ما إذا كان المستخدم يبدأ محادثة جديدة أو يعود إلى محادثة قائمة:
Referral postback payloads — both forms
// Form 1: New conversation — arrives as a standalone referral event// (user has never messaged your Page before, or is starting fresh)
{
"referral": {
"ref": "EMAIL_CAMPAIGN_MAY", // your ?ref= parameter"source": "SHORTLINK", // 'SHORTLINK' | 'ADS' | 'CUSTOMER_CHAT_PLUGIN'"type": "OPEN_THREAD"// always "OPEN_THREAD" for user-initiated
}
// Note: no postback.payload — this is a pure referral with no button
}
// Form 2: Button click with referral context (e.g. persistent menu from ad landing)// Arrives as a postback event with BOTH payload AND referral
{
"postback": {
"title": "Get Started",
"payload": "GET_STARTED",
"referral": {
"ref": "AD_SUMMER_SALE",
"source": "ADS",
"type": "OPEN_THREAD"
}
}
// postback.payload = the button action// postback.referral.ref = the campaign tracking string
}
لاستقبال أحداث الإحالة، اشترك في messaging_referrals. هذا اشتراك webhook ثالث منفصل عن messages وmessaging_postbacks. بدونه، لن تصل أبداً أحداث الإحالة من الشكل 1 (المحادثات الجديدة من البداية الباردة). أما الشكل 2 (postback + إحالة) فيصل مع الاشتراك في messaging_postbacks. اشترك في الثلاثة جميعاً للتغطية الكاملة.
نظام التوجيه الإنتاجي: بنية معالِجات مُهيكلة
عبارة switch على الـ payload تنجح مع 5 أزرار. أمّا عند 50 زراً موزّعة على 10 تدفّقات، فتصبح غير قابلة للصيانة. إليك النمط الإنتاجي: سجلّ توجيه يربط بادئات الـ payload بوحدات المعالج، مع دعم لـ payloads بصيغة JSON.
عندما تستقبل أحداث Messenger عبر SocialHook، تكون الـ postbacks مُستخرَجة مسبقاً وتصل بالتنسيق الموحّد نفسه مثل جميع أنواع الأحداث الأخرى. التداخل الخام entry[0].messaging[0].postback يكون قد فُكَّ بالفعل:
SocialHook normalized postback event
// Postback button tap:
{
"platform": "facebook",
"event": "postback",
"from": "12345678901234", // PSID"postback": {
"payload": "TRACK_ORDER", // your payload — ready to route"title": "Track My Order"// button label
},
"signature_verified": true
}
// Quick reply payload:
{
"platform": "facebook",
"event": "message.received",
"from": "12345678901234",
"message": {
"type": "quick_reply",
"payload": "CONFIRM_YES", // extracted from message.quick_reply.payload"body": "Yes, please!"// the quick reply title text
},
"signature_verified": true
}
// Your handler:
app.post('/webhook', async (req, res) => {
res.sendStatus(200);
const { event, from, postback, message } = req.body;
if (event === 'postback') awaitroutePostback(from, postback.payload);
if (message?.type === 'quick_reply') awaitroutePostback(from, message.payload);
});
FAQ
أسئلة شائعة
ما هو Facebook Messenger postback؟
الـ postback هو حدث webhook يُطلَق عندما ينقر المستخدم على زر Postback أو على عنصر القائمة الدائمة. تُهيّئ سلسلة payload على الزر — وعند النقر، ترسل Facebook تلك السلسلة بالضبط إلى الـ webhook الخاص بك في event.postback.payload. وهي الطريقة التي يستقبل بها الـ bot الخاص بك إشارات نوايا مُهيكلة من نقرات أزرار المستخدم، دون الحاجة إلى NLP أو تحليل نصي.
لماذا لا تصل postbacks Messenger إلى الـ webhook الخاص بي؟
السبب الأكثر احتمالاً: لقد اشتركت في messages لكن ليس في messaging_postbacks. وهما اشتراكان منفصلان. الحل: Messenger Settings ← Webhooks ← Edit Subscriptions ← فعّل messaging_postbacks. التحقّق الثاني: أن يكون زرّك من type: 'postback' (وليس type: 'web_url'). التحقّق الثالث: ألّا يكون حقل payload فارغاً (الـ payloads الفارغة تُسبّب رفضاً صامتاً).
ما الفرق بين postback وquick reply في Messenger؟
كلاهما يُسلّم سلسلة payload عند النقر، لكنهما يختلفان في ثلاثة جوانب: (1) الموقع في الـ webhook: postback ← event.postback.payload؛ quick reply ← event.message.quick_reply.payload. (2) المظهر: أزرار postback تبقى في المحادثة؛ أما quick replies فتختفي بعد نقرة واحدة. (3) الاشتراك: postbacks تحتاج إلى messaging_postbacks؛ أما quick replies فتصل مع messages.
هل يمكنني استخدام JSON كحمولة postback في Messenger؟
نعم — استخدم JSON.stringify() على كائن وضع السلسلة الناتجة كـ payload. وعند الاستقبال، استخدم JSON.parse() لإعادتها. تتعامل Facebook مع الـ payload كسلسلة معتمة بطول حتى 1,000 حرف. استخدم payloads بصيغة JSON عندما تحتاج إلى تمرير سياق مُهيكل (productId، الكمية، حالة التدفّق) لا يمكن استنتاجه من حالة المحادثة وحدها. استخدم JSON مدمجاً: أزِل الفراغات بـ JSON.stringify(obj) (دون وسيطة المسافات).
كيف أتتبّع أيّ حملة جلبت مستخدماً إلى Messenger bot الخاص بي؟
استخدم postbacks الإحالة. أضف وسيطة ?ref=YOUR_CAMPAIGN_ID إلى رابط m.me الخاص بك (مثلاً m.me/yourpage?ref=EMAIL_MAY). عندما يفتح المستخدم Messenger، يُطلَق حدث إحالة يحمل سلسلة ref الخاصة بك في event.referral.ref. اشترك في messaging_referrals لاستقبال أحداث الإحالة من البداية الباردة. وإذا نقر المستخدم على زر بعد وصوله، تظهر بيانات الإحالة جنباً إلى جنب مع الـ postback في event.postback.referral.ref.
كيف أُعدّ قائمة Messenger دائمة؟
أرسل POST مرة واحدة إلى graph.facebook.com/v21.0/me/messenger_profile مع Page Access Token الخاص بك ومصفوفة persistent_menu. لكل عنصر type (postback أو web_url)، وtitle، وإمّا payload (postback) أو url (web_url). تنطبق التغييرات عالمياً على جميع المستخدمين. ولن تحتاج إلى استدعاء هذه الـ API إلا عند تحديث القائمة — فهي تبقى حتى تُغيّرها. وأحداث postback من عناصر القائمة تصل بشكل مطابق لـ postbacks أزرار القوالب.
اربط صفحة Facebook الخاصة بك بـ SocialHook. كل postback يصل إلى المعالج لديك مع الـ payload مُستخرَجة بالفعل — دون تحليل خام للـ webhook، ودون تصحيح الاشتراكات، ودون أيّ كود HMAC تكراري. فقط event.postback.payload بالضبط متى احتجته.