شرح Facebook Page-Scoped ID (PSID) — مخطط PSID مقابل ASID مقابل IGSID، موقع PSID في حمولة webhook عند sender.id، البحث عبر Graph API، العزل بين الصفحات
في هذا الدليل: ما هو PSID · PSID مقابل ASID مقابل IGSID · مبدأ العزل · أين يقع PSID في حمولات webhook · إرسال الرسائل باستخدام PSID · البحث عبر Graph API · ID Matching API · حالة إعادة تعيين PSID · فهرسة قاعدة البيانات · التنسيق الموحّد في SocialHook

ما هو Facebook Page-Scoped ID (PSID)؟

إن Page-Scoped ID (PSID) هو سلسلة رقمية فريدة تُسنِدها Facebook لتمثيل مستخدم Facebook محدّد ضمن سياق صفحة Facebook محدّدة. وهو الطريقة التي يتعرّف بها تطبيقك على هوية مَن أرسل رسالة عبر Facebook Messenger.

إليك ما يميّزه عن معرّف المستخدم العادي:

  • PSID ليس معرّف Facebook الحقيقي للمستخدم. فالمعرّفات الحقيقية في Facebook داخلية بالنسبة لأنظمة Meta.
  • PSID خاص بالصفحة. فالمستخدم نفسه لديه PSID مختلف لكل صفحة Facebook يتفاعل معها عبر Messenger.
  • PSID ثابت لزوج مستخدم-صفحة معيّن — حتى يقوم المستخدم بحظر صفحتك ثم رفع الحظر عنها (انظر حالة إعادة التعيين أدناه).
  • PSID مبهم — فهو لا يحمل أي معلومات جوهرية عن المستخدم. إنه مجرّد معرّف مرجعي.

عملياً: عندما يرسل عميل رسالة عبر Facebook Messenger إلى نشاطك التجاري، فإن PSID الخاص به هو المعرّف الذي يستخدمه تطبيقك لإرسال الردود، والبحث في سجلّه السابق، وتخزين سجل محادثته. إنه المفتاح الأساسي لأي مستخدم Messenger في نظامك.

PSID وASID وIGSID: أنواع معرّفات المستخدمين الثلاثة في Facebook

تحتفظ Facebook بثلاثة أنظمة مختلفة من المعرّفات المخصّصة لثلاثة سياقات مختلفة. ومعرفة أيها تعمل عليه يحميك من فئة كاملة من الأخطاء التي قد تخلط فيها — دون قصد — بين معرّفات المستخدمين.

Messenger
Page-Scoped ID (PSID)
يُعرِّف المستخدم في سياق صفحة Facebook محدّدة. يظهر في أحداث webhook الخاصة بـ Messenger في الحقل sender.id. يُستخدم لإرسال الرسائل عبر Send API. مختلف لكل صفحة يتواصل معها المستخدم.
sender.id في webhook
Facebook Login / تطبيقات اجتماعية
App-Scoped ID (ASID)
يُعرِّف المستخدم في سياق تطبيق Facebook محدّد. يُعاد عندما يسجّل المستخدمون الدخول عبر Facebook Login (OAuth). مختلف لكل تطبيق يُصادق عليه المستخدم. يُستخدم في استدعاءات Graph API للتطبيقات الاجتماعية.
يُعاد بواسطة Facebook Login
Instagram DM
Instagram-Scoped ID (IGSID)
يُعرِّف المستخدم في سياق حساب Instagram Business محدّد. يظهر في أحداث webhook الخاصة برسائل Instagram DM. مفهوم مشابه لـ PSID ولكن لـ Instagram. يُستخدم لإرسال الردود عبر Instagram Messaging API.
sender.id في webhook الخاص بـ IG
لا تخلط أبداً بين هذه المعرّفات. فمحاولة إرسال رسالة Messenger إلى ASID تُعيد خطأً. ومحاولة استخدام PSID من صفحة Facebook معيّنة لإرسال رسالة عبر صفحة أخرى تفشل أيضاً — فـ PSIDs مخصّصة بالصفحات ولا يمكن استخدامها إلا مع Page Access Token الخاص بالصفحة التي ولّدتها. وتخزين أنواع مختلفة من المعرّفات في نفس عمود قاعدة البيانات دون تسمية النوع هو خطأ شائع.

مبدأ العزل: لماذا تختلف PSIDs لكل صفحة

صمّمت Facebook نظام PSID خصيصاً لمنع تتبّع المستخدمين عبر المنصّات. فلو حصل كل تطبيق ومشغّل صفحة على معرّف المستخدم نفسه، لتمكّن سماسرة البيانات والجهات الخبيثة من ربط هويّات المستخدمين عبر مئات الأنشطة التجارية المختلفة — وهو بالضبط نوع جمع البيانات عبر الخدمات الذي أدّى إلى فضيحة Cambridge Analytica.

يعني العزل ما يلي: إذا كان نشاطك التجاري يدير صفحتَي Facebook، فإن مستخدم Facebook نفسه لديه PSIDs مختلفة في كل صفحة. ولا يمكنك استخدام PSID الخاص بالصفحة A للبحث عن نشاط على الصفحة B دون موافقة صريحة من المستخدم وبدون ID Matching API (الذي يُغطَّى أدناه).

نفس مستخدم Facebook
Alice Johnson (معرّف FB الحقيقي: مخفي عنك)
صفحة متجرك الرئيسية
PSID: 12345678901234
صفحة الدعم الخاصة بك
PSID: 98765432109876
صفحة شريكك
PSID: 11223344556677

لدى Alice ثلاثة PSIDs مختلفة تماماً عبر ثلاث صفحات. ولا يمكن أي ربط بينها انطلاقاً من البيانات وحدها. هذا تصميمٌ متعمَّد — ولهذا فإن بناء عمليات Messenger متعدّدة الصفحات يتطلّب تفكيراً دقيقاً حول أي PSID ينتمي إلى أي سياق صفحة.

أين يقع PSID في حمولات webhook الخاصة بـ Messenger

عندما يرسل مستخدم رسالة إلى صفحة Facebook الخاصة بك، تُطلق منصّة Messenger طلب HTTP POST إلى عنوان URL المسجّل لـ webhook لديك. يقع PSID في entry[0].messaging[0].sender.id ضمن الحمولة الخام. وفيما يلي البنية الكاملة:

Facebook Messenger webhook payload
messenger-webhook.json
{ "object": "page", // always "page" for Messenger webhooks "entry": [{ "id": "PAGE_ID", // your Facebook Page ID (not a PSID) "time": 1747231892123, "messaging": [{ "sender": { "id": "12345678901234" // ← THIS IS THE PSID — the user who sent the message }, "recipient": { "id": "PAGE_ID" // your Page ID — same as entry.id }, "timestamp": 1747231892100, "message": { "mid": "m_abc123...", // unique message ID "text": "Hello! I need help with my order." } }] }] } // PSID extraction path: body.entry[0].messaging[0].sender.id // Page ID path: body.entry[0].id (same as recipient.id)

استخراج PSID بشكل صحيح:

Node.js — PSID extraction from raw webhook
extractPsid.js
app.post('/webhook/facebook', (req, res) => { res.sendStatus(200); // acknowledge first const body = req.body; if (body.object !== 'page') return; body.entry.forEach(entry => { const pageId = entry.id; // your Page ID entry.messaging.forEach(event => { const psid = event.sender.id; // ← the PSID const pageId = event.recipient.id; // confirms which Page received it if (event.message) { console.log(`Message from PSID ${psid} on page ${pageId}:`, event.message.text); handleMessage(psid, pageId, event.message); } else if (event.postback) { handlePostback(psid, pageId, event.postback); } else if (event.read) { handleRead(psid, event.read.watermark); } }); }); });

إرسال الردود باستخدام PSID

للردّ على مستخدم، تُرسل طلب POST إلى Send API مستخدماً PSID الخاص به كمستلِم. PSID هو المعرّف الوحيد الذي تحتاجه — وتتولّى Facebook تحديد المستخدم انطلاقاً منه وتسليم الرسالة إلى صندوق Messenger الخاص به.

Node.js — send Messenger message by PSID
sendByPsid.js
const GRAPH = 'https://graph.facebook.com/v21.0'; const PAGE_ACCESS_TOKEN = process.env.FB_PAGE_ACCESS_TOKEN; // must match the Page that got the PSID async function sendMessengerMessage(psid, messageText) { const res = await fetch( `${GRAPH}/me/messages?access_token=${PAGE_ACCESS_TOKEN}`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ recipient: { id: psid }, // PSID goes here message: { text: messageText }, messaging_type: 'RESPONSE', // within 24h window }), } ); if (!res.ok) { const err = await res.json(); if (err.error?.code === 551 || err.error?.code === 100) { // PSID may be invalid/reset — user may have blocked and unblocked throw Object.assign(new Error('Invalid PSID'), { code: 'PSID_INVALID', psid }); } throw new Error(`Messenger send error: ${JSON.stringify(err)}`); } return await res.json(); // { recipient_id: psid, message_id: "m_..." } } // Critical: use the Page Access Token from the Page that the user's PSID belongs to. // Using a different Page's token returns an error even if the PSID format is correct.

البحث عن معلومات المستخدم عبر Graph API باستخدام PSID

باستخدام PSID وPage Access Token الخاص بك، يمكنك الاستعلام من Graph API لاسترجاع المعلومات العامة للملف الشخصي للمستخدم — وتحديداً اسمه الأول واسم العائلة وصورة الملف الشخصي. وهذا مفيد لتخصيص ردود البوت الخاص بك وملء سجلّات CRM مسبقاً.

الصلاحيات المطلوبة: يجب أن يحصل تطبيق Facebook الخاص بك على موافقة على صلاحية pages_messaging لقراءة الملف الشخصي للمستخدم عبر PSID. وبدونها، يُعيد Graph API الحقل id فقط. كما تتطلّب حقول الملف الشخصي (الاسم، الصورة) أن يكون المستخدم قد راسل صفحتك بشكل صريح أولاً — لا يمكنك البحث عن PSIDs اعتباطية، فقط تلك التي بدأت محادثة مع صفحتك.
Node.js — Graph API user lookup by PSID
lookupByPsid.js
async function getUserProfile(psid) { const fields = 'first_name,last_name,profile_pic,locale,timezone,gender'; const url = `${GRAPH}/${psid}?fields=${fields}&access_token=${PAGE_ACCESS_TOKEN}`; const res = await fetch(url); if (!res.ok) throw new Error(`Profile lookup failed: ${await res.text()}`); return await res.json(); // Returns: { id, first_name, last_name, profile_pic, locale, timezone } } // Usage — look up profile on first contact and store it async function onFirstContact(psid) { const profile = await getUserProfile(psid); await upsertUser({ psid, firstName: profile.first_name, lastName: profile.last_name, profilePic: profile.profile_pic, locale: profile.locale, // e.g. "pt_BR" — useful for language routing }); } // Note: gender and timezone fields deprecated by Meta in v18.0+ // Stick to first_name, last_name, profile_pic, locale for future-safe code

ID Matching API: التحويل بين PSID وASID

في بعض الحالات قد تحتاج إلى ربط مستخدم Messenger (المُعرَّف بـ PSID) بالمستخدم نفسه الذي صادق عبر Facebook Login (المُعرَّف بـ ASID). توفّر Facebook ID Matching API لهذا الغرض — ولكنّه يتطلّب صلاحية pages_user_ids ولا يعمل إلا عبر التطبيقات التابعة لنفس Business Portfolio.

حالة الاستخدام النموذجية: مستخدم صادق عبر Facebook Login (مما يمنحك ASID الخاص به) ثم تواصل معك لاحقاً عبر Messenger (مما يمنحك PSID الخاص به). تريد أن تربط كلا المعرّفين بنفس سجل المستخدم في قاعدة بياناتك.

Node.js — ID Matching API (PSID ↔ ASID)
idMatching.js
// Convert PSID → ASID (to find the same user who logged in via Facebook Login) async function getAsidFromPsid(psid) { const url = `${GRAPH}/${psid}?fields=ids_for_apps&access_token=${PAGE_ACCESS_TOKEN}`; const res = await fetch(url); const data = await res.json(); // Returns array of app-scoped IDs for apps in the same Business Portfolio const appIds = data.ids_for_apps?.data ?? []; const match = appIds.find(a => a.app.id === process.env.FB_APP_ID); return match?.id; // the ASID for this user on your app } // Convert ASID → PSID (to find the Messenger identity of a logged-in user) async function getPsidFromAsid(asid) { const url = `${GRAPH}/${asid}?fields=ids_for_pages&access_token=${PAGE_ACCESS_TOKEN}`; const res = await fetch(url); const data = await res.json(); const pageIds = data.ids_for_pages?.data ?? []; const match = pageIds.find(p => p.page.id === process.env.FB_PAGE_ID); return match?.id; // the PSID for this user on your Page } // Requirements: // - App must have pages_user_ids permission (requires App Review) // - Both apps must be under the same Business Portfolio // - User must have interacted with both the Page and the App

حالة إعادة تعيين PSID التي يجب أن تعالجها

هناك سيناريو واحد يتغيّر فيه PSID الخاص بالمستخدم: إذا قام المستخدم بحظر صفحة Facebook الخاصة بك ثم قام لاحقاً برفع الحظر عنها. فعند رفع الحظر ومراسلتك من جديد، يحصل على PSID جديد تماماً خاص بصفحتك. ويصبح PSID القديم لديه يتيماً — وأي رسالة تحاول إرسالها إليه ستفشل.

من السهل تفويت هذه الحالة لأنها تحدث بصمت. فالمستخدم لا يعلم أن PSID الخاص به قد تغيّر. وقاعدة بياناتك لا تزال تحتوي على PSID القديم. وعندما تحاول إرسال رسالة إليه، تحصل على خطأ. وفيما يلي كيفية معالجتها بشكل صحيح:

Node.js — PSID reset detection and update
psidReset.js
async function handleInboundMessage(event) { const psid = event.sender.id; const message = event.message; // Look up this PSID in your database let user = await db.findByPsid(psid); if (!user) { // New PSID — either genuinely new user or a PSID reset after block/unblock // Check if we have a user with matching profile (name + profile pic) const profile = await getUserProfile(psid); const existing = await db.findByProfile(profile.first_name, profile.last_name); if (existing) { // Likely a PSID reset — update the stored PSID await db.updatePsid(existing.id, psid); user = { ...existing, psid }; console.log(`PSID reset detected for user ${existing.id} — new PSID: ${psid}`); } else { // Genuinely new user user = await db.createUser({ psid, ...profile }); } } await processMessage(user, message); } // Also: subscribe to the messaging_optins and messaging_optouts webhook events // messaging_optouts fires when a user blocks your page — flag the PSID as potentially stale

فهرسة قاعدة البيانات: الطريقة الصحيحة لتخزين PSIDs

تتسبّب أخطاء تخزين PSID في أخطاء خفية يصعب تصحيحها. وفيما يلي الأنماط الصحيحة:

  • لا تستخدم PSID أبداً كمفتاح أساسي. فـ PSIDs يمكن أن تتغيّر (سيناريو الحظر/رفع الحظر). استخدم رقم تسلسلي داخلي أو UUID كمفتاح أساسي. PSID هو معرّف خارجي يرتبط بالمستخدم الداخلي في نظامك.
  • قم دائماً بتخزين Page ID جنباً إلى جنب مع PSID. فـ PSID له معنى فقط في سياق الصفحة التي وُلِّد من أجلها. وإذا كنت تدير عدة صفحات، يجب أن يكون (psid, page_id) قيداً فريداً مركباً، وليس psid وحده.
  • قم بفهرسة عمود PSID. فكل رسالة واردة تتطلّب بحثاً عن PSID. وبدون فهرس، يعني ذلك مسحاً كاملاً للجدول عند كل حدث webhook.
  • خزّن PSID كسلسلة نصية. فـ PSIDs سلاسل رقمية لكنها قد تتجاوز نطاق الأعداد الصحيحة الآمنة في JavaScript (Number.MAX_SAFE_INTEGER). تعامل معها دائماً كسلاسل، لا كأرقام.
SQL — correct PSID schema
schema.sql
CREATE TABLE messenger_users ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), psid VARCHAR(25) NOT NULL, -- string, not bigint page_id VARCHAR(25) NOT NULL, -- which Facebook Page generated this PSID first_name VARCHAR(100), last_name VARCHAR(100), profile_pic TEXT, locale VARCHAR(10), -- e.g. 'pt_BR', 'en_US' asid VARCHAR(25), -- linked App-Scoped ID (Facebook Login), nullable is_active BOOLEAN DEFAULT TRUE, -- false if PSID became invalid created_at TIMESTAMPTZ DEFAULT NOW(), updated_at TIMESTAMPTZ DEFAULT NOW(), UNIQUE (psid, page_id) -- composite unique — PSID is page-scoped ); CREATE INDEX idx_messenger_users_psid ON messenger_users (psid); CREATE INDEX idx_messenger_users_page_id ON messenger_users (page_id); CREATE INDEX idx_messenger_users_asid ON messenger_users (asid) WHERE asid IS NOT NULL; -- Never store PSID as INTEGER or BIGINT -- Example PSID: '1234567890123456789' — exceeds JS Number.MAX_SAFE_INTEGER -- parseInt('1234567890123456789') === 1234567890123456800 — wrong!

PSID في أحداث Facebook Messenger الموحّدة في SocialHook

عندما تستخدم SocialHook لـ Facebook Messenger، يُستخرج PSID مسبقاً من غلاف webhook الخام الخاص بـ Messenger ويُسلَّم في المستوى الأعلى من الحدث الموحّد. لا حاجة للتنقّل عبر المسار المتداخل entry[0].messaging[0].sender.id:

الفائدة الأساسية للنشر متعدّد القنوات: تقوم SocialHook بتوحيد PSID (Facebook) ورقم الهاتف (WhatsApp) وIGSID (Instagram) كلّها في نفس الحقل from وبنفس شكل الحمولة. ويمكن لكود تطبيقك التعامل مع القنوات الثلاث كلّها بدالة معالجة رسائل واحدة، مع التمييز بينها عبر platform عند الحاجة. اطّلع على مرجع الحمولة للحصول على المقارنة الكاملة للمخطّط عبر القنوات الثلاث.

أسئلة شائعة

ما هو Facebook Page-Scoped ID (PSID)؟
PSID هو سلسلة فريدة ومبهمة تُسنِدها Facebook لتمثيل مستخدم محدّد ضمن سياق صفحة Facebook محدّدة. فعندما يرسل شخص ما رسالة Messenger إلى صفحة Facebook الخاصة بك، تتعرّف Facebook عليه بالنسبة لتطبيقك باستخدام PSID الخاص به — وليس معرّف Facebook الحقيقي. فالشخص نفسه لديه PSID مختلف لكل صفحة يتفاعل معها، مما يوفّر عزلاً للخصوصية بين مشغّلي الصفحات.
أين يظهر PSID في حمولة webhook الخاصة بـ Facebook Messenger؟
في webhook الخام الخاص بـ Messenger: body.entry[0].messaging[0].sender.id. وفي تنسيق SocialHook الموحّد: event.from. ويكون sender.id دائماً PSID الخاص بالشخص الذي أرسل الرسالة. أما recipient.id فهو Page ID الخاص بك (وليس PSID). وعند التكرار على مصفوفات entries وmessaging المتعدّدة، استخدم دائماً messaging[n].sender.id، وليس entry[n].id.
هل يمكن أن يتغيّر PSID الخاص بالمستخدم؟
نعم — عندما يقوم المستخدم بحظر صفحة Facebook الخاصة بك ثم يرفع الحظر عنها لاحقاً، فإنه يحصل على PSID جديد لصفحتك. ويصبح PSID القديم غير صالح. ويحدث ذلك بصمت. تحقّق دائماً من أخطاء PSID_INVALID عند إرسال الرسائل، وحدِّث قاعدة بياناتك عند استلام PSID جديد من مستخدم يتطابق ملفه الشخصي مع سجل موجود. ولهذا السبب لا تستخدم PSID أبداً كمفتاح أساسي.
هل يمكنني إرسال رسالة Messenger إلى مستخدم لم أتلقَّ منه أي رسالة من قبل؟
لا، ليس عبر Send API القياسي بدون PSID. تحتاج إلى أن يبدأ المستخدم التواصل (مما يمنحك PSID الخاص به)، أو تحتاج إلى الحصول على PSID الخاص به عبر ID Matching API (إذا كان قد استخدم Facebook Login على تطبيقك ولديك صلاحية pages_user_ids). ولا يمكنك البحث عن PSIDs لمستخدمين عشوائيين — فـ PSIDs لا تتواجد إلا في سياق مستخدم تفاعل مع صفحتك المحدّدة. وهذا تصميم متعمَّد لحماية الخصوصية.
لماذا يجب تخزين PSID كسلسلة نصية وليس كعدد صحيح؟
PSIDs سلاسل رقمية يمكن أن تتجاوز Number.MAX_SAFE_INTEGER في JavaScript (9,007,199,254,740,991). فإذا قمت بتحليل PSID مثل "1234567890123456789" كعدد صحيح في JavaScript، تحصل على فقدان دقّة: parseInt('1234567890123456789') === 1234567890123456800 — تكون الأرقام الأخيرة خاطئة. خزِّن ومرِّر دائماً PSIDs كسلاسل في قاعدة بياناتك (VARCHAR/TEXT) وفي كودك. ولا تحوّلها إلى أرقام أبداً.
كيف يختلف PSID عن ASID؟
PSID (Page-Scoped ID) مخصّص لـ Messenger — يُعرِّف المستخدم لكل صفحة Facebook. أما ASID (App-Scoped ID) فهو لـ Facebook Login — يُعرِّف المستخدم لكل تطبيق Facebook. فالمستخدم نفسه لديه PSID مختلف لكل صفحة، وASID مختلف لكل تطبيق. يمكنك التحويل بينهما باستخدام ID Matching API، ولكن فقط إذا كانت كل من الصفحة والتطبيق ضمن نفس Business Portfolio ولديك صلاحية pages_user_ids.

PSIDs تُسلَّم نظيفة.
لا حاجة لأي تحليل.

اربط صفحة Facebook الخاصة بك بـ SocialHook واستقبل كل حدث في Messenger مع PSID مستخرَجاً مسبقاً في event.from — جنباً إلى جنب مع أحداث WhatsApp وInstagram بنفس التنسيق الموحّد. معالج واحد للقنوات الثلاث.

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