Facebook Messenger postbacks — تدفّق بيانات نقر الزر من واجهة Messenger إلى event.postback.payload في الـ webhook، مخطط نظام التوجيه
في هذا الدليل: ما هو الـ 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 الخاصة بك

أصلِحه في موضعين:

  • لوحة المطوّر: Messenger ← Settings ← Webhooks ← Edit Subscriptions ← فعّل 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
function extractPayload(event) { // 1. Standard postback button or persistent menu tap if (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 payload if (event.message?.text) { return { type: 'TEXT', payload: null, text: event.message.text, }; } return { type: 'UNKNOWN', payload: null }; }

أنماط تصميم الـ payload: الجيّد مقابل السيّئ

سلسلة الـ payload مِلْكٌ لك لتصمّمها — فإن Facebook يتعامل معها كسلسلة معتمة. وطريقة تصميمك للـ payloads هي التي تحدّد مدى قابلية الصيانة وقابلية التصحيح في منطق الـ bot الخاص بك عند التوسّع. إليك الأنماط التي تنجح وتلك التي تُسبّب المشاكل:

✓ جيّد — سلاسل مسطّحة بمساحات أسماء
بادئة الإجراء + السياق
FLOW_TRACK_ORDER
FLOW_GET_SUPPORT
PRODUCT_VIEW_456
CONFIRM_YES
CONFIRM_NO
سهلة التوجيه باستخدام startsWith() أو switch. تخبرك البادئة بالتدفّق، واللاحقة تخبرك بالاختيار. مقروءة في السجلّات. وقصيرة بما يكفي للبقاء تحت حدّ 1,000 حرف بأمان.
✓ جيّد — JSON للـ payloads الغنية بالسياق
بيانات مُهيكلة عند الحاجة
{"action":"ADD_TO_CART",
"productId":"SKU-456",
"qty":1}
استخدم 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.

Node.js — production postback router
postbackRouter.js
const orderHandlers = require('./handlers/orders'); const supportHandlers = require('./handlers/support'); const productHandlers = require('./handlers/products'); const menuHandlers = require('./handlers/menu'); // Prefix-based routing table const POSTBACK_ROUTES = { 'FLOW_TRACK': orderHandlers.startTracking, 'FLOW_RETURN': orderHandlers.startReturn, 'SUPPORT_': supportHandlers.route, // SUPPORT_BILLING, SUPPORT_SHIPPING, etc. 'PRODUCT_': productHandlers.route, 'MENU_': menuHandlers.route, 'GET_STARTED': menuHandlers.getStarted, 'CONFIRM_': menuHandlers.handleConfirm, }; async function routePostback(psid, event) { // Extract raw payload string let rawPayload = event.postback?.payload || event.message?.quick_reply?.payload; if (!rawPayload) return; // Attempt JSON parse for structured payloads let payload = rawPayload; let parsedData = null; try { parsedData = JSON.parse(rawPayload); payload = parsedData.action || rawPayload; // use 'action' field as the routing key } catch (_) { /* not JSON — use raw string */ } // Handle referral tracking (if present alongside postback) if (event.postback?.referral || event.referral) { const ref = event.postback?.referral || event.referral; await trackReferral(psid, ref.ref, ref.source); // log campaign attribution } // Find handler by exact match or prefix let handler = POSTBACK_ROUTES[payload]; // exact match first if (!handler) { // Try prefix matching for (const [prefix, fn] of Object.entries(POSTBACK_ROUTES)) { if (payload.startsWith(prefix)) { handler = fn; break; } } } if (!handler) { console.warn(`Unhandled postback payload: ${rawPayload}`); return; } await handler(psid, { payload: rawPayload, parsedData, // null if not JSON title: event.postback?.title, source: 'postback', }); } module.exports = { routePostback };

SocialHook: postbacks تصل بصيغة موحّدة

عندما تستقبل أحداث Messenger عبر SocialHook، تكون الـ postbacks مُستخرَجة مسبقاً وتصل بالتنسيق الموحّد نفسه مثل جميع أنواع الأحداث الأخرى. التداخل الخام entry[0].messaging[0].postback يكون قد فُكَّ بالفعل:

أسئلة شائعة

ما هو 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 أزرار القوالب.

payloads مُستخرَجة مسبقاً.
جاهزة للتوجيه.

اربط صفحة Facebook الخاصة بك بـ SocialHook. كل postback يصل إلى المعالج لديك مع الـ payload مُستخرَجة بالفعل — دون تحليل خام للـ webhook، ودون تصحيح الاشتراكات، ودون أيّ كود HMAC تكراري. فقط event.postback.payload بالضبط متى احتجته.

لا حاجة إلى بطاقة ائتمان · 50$ شهرياً بعد التجربة · Messenger + WhatsApp + Instagram