توثيق المطوّرين
خطافات الويب (Webhooks) مُفعّلة اليوم على باقة Scale، بعمليات إرسال موقّعة تُعاد المحاولة فيها ويمكنك التحقق منها. وكذلك مفاتيح API التي تستدعي بها أنظمتك نقاط التكامل: إليك كيف تستقبل خطافات الويب وكيف تبدأ بمفاتيح API.
خطافات الويب (Webhooks) ميزة مُفعَّلة وتعمل فعليًا على VEXORS اليوم، ومتاحة على باقة Scale. أما استدعاء VEXORS من أنظمتك الخاصة بمفتاح API فميزة منفصلة، ومتاحة كذلك على باقة Scale.
خطافات الويب (Scale، متاحة الآن)
اضبط خطافات الويب من الإعدادات ثم خطافات الويب (لمديري الشركة فقط). من هناك يمكنك:
- إضافة نقطة نهاية: اسم، ورابط HTTPS ليستدعيه VEXORS، والأحداث التي تريدها من الكتالوج (أحداث الطلبات والعروض والترسية والتقييمات والفرص وعروض الخدمات، مثل
bid.submittedوaward.issued). حتى 20 نقطة نهاية لكل شركة. تستقبل نقطة النهاية لديك الحدث فقط عندما تكون شركتك من الأطراف التي يُخطرها VEXORS به داخل التطبيق: يستقبل المشتريbid.submittedللعروض المقدَّمة على طلباته، ويستقبل المورّدbid.shortlistedلعرضه هو، والحدث الذي يخصّ الطرفين مثلaward.issuedيصل إلى كليهما. الطلب المنشور يصل فقط إلى المورّدين الذين يظهر لهم: فإذا كان الطلب خاصًا فهم المورّدون المدعوّون. وعند إغلاق طلب أو إعادة فتحه يُبلَّغ كذلك المورّدون الذين لديهم عرض في حالة مسودة عليه. - نسخ سرّ التوقيع الخاص بنقطة النهاية في المرة الوحيدة التي يُعرض فيها. كل عملية إرسال موقّعة به (انظر أدناه). يمكنك تجديده لاحقًا؛ يُعرض السرّ الجديد مرة واحدة ويتوقف القديم عن العمل في تلك اللحظة، فحدّث المستقبِل لديك أولًا. وإذا جدّد شخصان سرّ نقطة النهاية نفسها في اللحظة ذاتها، يتم تجديد واحد فقط ويُطلب من الآخر إعادة تحميل الصفحة.
- إرسال إرسال اختباري ورؤية النتيجة فورًا: رمز استجابة HTTP والمدة والترويسات التي أرسلها VEXORS. يعمل الاختبار أيضًا على نقطة نهاية معطّلة: يُرسَل فعليًا، وتبقى نقطة النهاية معطّلة، ولا يغيّر الاختبار حالة نقطة النهاية أبدًا.
- فتح قائمة عمليات الإرسال لنقطة نهاية: كل عملية إرسال بحالتها ومحاولاتها ورمز الاستجابة والحمولة كما أُرسلت (مع حجب القيم الحساسة)، مع إمكانية إعادة إرسال أي عملية منتهية. إعادة الإرسال هي إرسال جديد للحدث نفسه، مرتبط بالإرسال الأصلي.
- تعطيل نقطة النهاية أو تفعيلها أو حذفها. التعطيل أو الحذف يلغي عمليات الإرسال التي كانت ما زالت تنتظر؛ وبعد إعادة تفعيل نقطة النهاية يمكنك إعادة إرسالها من قائمة عمليات الإرسال.
هذه الصفحة مقصورة على Scale. وعلى Grow وExplore تعرض دعوة للترقية بدلًا منها.
استقبال خطافات الويب
كل عملية إرسال هي طلب POST عبر HTTPS إلى نقطة النهاية لديك بجسم JSON:
{
"id": "6f3b…", // معرّف الإرسال — نفسه في كل إعادة محاولة لهذا الإرسال
"event": "bid.submitted",
"event_id": "…", // الحدث المصدر؛ يبقى نفسه عبر إعادة المحاولات وعمليات إعادة الإرسال
"occurred_at": "2026-09-29T10:15:00.000Z", // وقت وقوع الحدث، لا وقت إرسال هذه المحاولة
"company_id": "…",
"delivery": { "attempt": 1, "redelivery_of": null },
"data": { … } // حمولة الحدث
}
ما الذي يحتويه data. فقط الحقول التي ينشرها VEXORS لذلك الحدث وللطرف الذي تمثّله فيه. لن تستقبل أبدًا قائمة الشركات الأخرى المطابقة لطلب، ولا معرّفات مستخدمي شركة أخرى أو أسماء أشخاصها، ولا موضع موافقة داخل سلسلة موافقات جهة أخرى. وإذا كان لديك عرض مسودة على طلب أُغلق أو أُعيد فتحه فإن الحدث يذكر مسودتك أنت فقط. أي حقل يضيفه VEXORS داخليًا لاحقًا لا يُرسَل إليك حتى يُنشَر لطرفك، فتعامل مع الحقول غير المعروفة على أنها اختيارية ولا تعتمد على حقول غير موثّقة لحدثك.
عرض مسحوب لم تره قط. الحدث bid.withdrawn يُعلِم المشتري بأن مورّدًا سحب عرضًا على أحد طلباته. إذا كان العرض قد قُدِّم إليك، يذكر الحدث المورّد والعرض. وإذا سحب المورّد عرضًا لم يصلك أصلًا (مسودة، أو عرض ما زال ينتظر الموافقة الداخلية لدى المورّد)، فإنك تستقبل الحدث كذلك، لكن data فيه يذكر فقط الطلب المعني ووقت السحب، مع علامة تفيد بأن العرض لم يكن ظاهرًا لك قط:
{
"request_id": "…",
"buyer_company_id": "…",
"rfx_ref": "…",
"withdrawn_at": "2026-09-30T09:00:00.000Z",
"was_visible_to_buyer": false
}
منذ 30 سبتمبر 2026 لم تعد الحقول التالية تُرسَل إلى الشركة التي كانت تستقبلها:
request.closed, request.reopened matched_company_ids, affected_draft_bids
request.evaluation-started matched_company_ids
request.details-updated matched_company_ids
request.approval-approved, -rejected step_id, step_number
bid.submitted assigned_am_user_id, performed_by_user_id, actor_name
rating.submitted rater_user_id, actor_name
ومن التاريخ نفسه، الأحداث التي تخصّ عرض المورّد أو دعوته أو سحب الترسية أو عرض الخدمة (bid.shortlisted وbid.unshortlisted وbid.under-review وbid.approval-approved وrequest.invitation-sent وrequest.invitation-rescinded وaward.rescinded وservice-offer.accepted وservice-offer.converted) وكذلك request.published تُرسَل إلى نقاط نهاية المورّد لا المشتري.
الترويسات في كل عملية إرسال
| الترويسة | المعنى |
|---|---|
X-Vexors-Delivery-Id | معرّف الإرسال. ثابت عبر إعادة محاولات الإرسال نفسه؛ استخدمه لتجاهل التكرارات. |
X-Vexors-Event | رمز الحدث، مثل bid.submitted. الإرسال الاختباري يحمل webhook.test. |
X-Vexors-Event-Id | معرّف الحدث الأصلي. إعادة الإرسال اليدوية تحمل معرّف إرسال جديدًا لكن بمعرّف الحدث نفسه، فاستخدم معرّف الحدث لتمييز حدث سبق أن عالجته. لكل واقعة معرّف حدث خاص بها: الطلب الذي يُعاد فتحه مرتين، أو المورّد الذي يُدعى مجددًا بعد سحب دعوته، يُرسَل عنه حدثان منفصلان. |
X-Vexors-Timestamp | الوقت بثواني Unix عند إرسال هذه المحاولة (كل إعادة محاولة تحمل وقتًا جديدًا). |
X-Vexors-Attempt | رقم المحاولة، يبدأ من 1. |
X-Vexors-Signature | t=<ثواني Unix>,v1=<hex HMAC-SHA256> — انظر التحقق أدناه. نقاط النهاية المُنشأة قبل سبتمبر 2026 تتلقى هنا الصيغة الأقدم sha256=<hex> إلى أن تبدّلها إلى v1 من تعديل. |
X-Vexors-Signature-V1 | في نقاط النهاية الأقدم فقط: التوقيع بصيغة t=…,v1=… حتى تنقل التحقق لديك قبل التبديل. |
X-Vexors-Redelivery-Of | في إعادة الإرسال اليدوية فقط: معرّف الإرسال الأصلي. |
X-Idempotency-Key | في نقاط النهاية الأقدم فقط: معرّف الحدث، كما كانت تتلقاه من قبل تمامًا. يتوقف عند التبديل إلى v1؛ استخدم X-Vexors-Event-Id بدلًا منه. |
User-Agent | Vexors-Webhooks/1.0 (+https://vexors.com) |
تحقّق من التوقيع. احسب HMAC-SHA256 بسرّ نقطة النهاية لديك على النص <t>.<raw body> — الوقت من الترويسة، ثم نقطة، ثم جسم الطلب كما وصل تمامًا (لا تحلّله ثم تعيد تسلسله أولًا) — وقارنه بقيمة v1 بمقارنة ثابتة الزمن. ارفض الإرسال إذا كان الوقت يبعد أكثر من 300 ثانية عن ساعتك. ثم سجّل معرّف الإرسال ومعرّف الحدث وتجاهل أي تكرار لأيٍّ منهما.
ترويسة التوقيع غير السليمة يجب أن تنتهي برفض الإرسال، لا بتعطّل خدمتك. أجب بالرمز 401 إذا كانت الترويسة غائبة، أو وصلت أكثر من مرة، أو كانت أطول من المعتاد، أو كان الوقت فيها ليس عددًا صحيحًا موجبًا، أو كان التوقيع ليس 64 خانة ست عشرية بالضبط، أو كان الوقت خارج المهلة المسموح بها لديك، أو لم يطابق التوقيع. المثال أدناه يتحقق من شكل الترويسة قبل أي مقارنة. وإذا كنت تقرأ مهلة الوقت من الإعدادات فتأكد أن غياب القيمة لا يعطّل الفحص؛ المثال يرجع إلى 300 ثانية.
import { createHmac, timingSafeEqual } from 'node:crypto';
// rawBody: جسم الطلب كما وصل (Buffer أو string) دون تعديل
export function verifyVexorsWebhook(rawBody, signatureHeader, secret, toleranceSeconds = 300) {
const match = /^t=(\d+),v1=([0-9a-f]{64})$/i.exec(signatureHeader ?? '');
if (!match) return false;
const t = Number(match[1]);
// مهلة ليست رقمًا تجعل كل وقت مقبولًا: ارجع إلى القيمة الافتراضية
const tolerance = Number.isFinite(toleranceSeconds) && toleranceSeconds >= 0 ? toleranceSeconds : 300;
if (Math.abs(Math.floor(Date.now() / 1000) - t) > tolerance) return false;
const expected = createHmac('sha256', secret).update(`${t}.${rawBody}`).digest('hex');
const a = Buffer.from(expected, 'hex');
const b = Buffer.from(match[2], 'hex');
return a.length === b.length && timingSafeEqual(a, b);
}
// مثال Express: احتفظ بالجسم الخام، تحقّق، ثم تجاهل التكرار بمعرّف الإرسال وبمعرّف الحدث
app.post('/vexors', express.raw({ type: 'application/json' }), (req, res) => {
if (!verifyVexorsWebhook(req.body, req.get('X-Vexors-Signature'), process.env.VEXORS_WEBHOOK_SECRET)) {
return res.status(401).end();
}
const deliveryId = req.get('X-Vexors-Delivery-Id'); // نفسه في كل إعادة محاولة للإرسال الواحد
const eventId = req.get('X-Vexors-Event-Id'); // نفسه عند إعادة الإرسال اليدوية للحدث
if (alreadyProcessed(deliveryId) || alreadyProcessed(eventId)) return res.status(200).end();
handle(JSON.parse(req.body.toString('utf8')));
markProcessed(deliveryId);
markProcessed(eventId);
res.status(200).end();
});
في نقطة النهاية الأقدم التي ما زالت على صيغة sha256=، القيمة المتوقعة هي HMAC-SHA256 على الجسم الخام وحده (بلا وقت)، وتُقارن بالطريقة نفسها؛ هذه الصيغة لا تحمي من إعادة التشغيل (replay)، فبدّل إلى v1 متى استطعت. وتحتفظ نقاط النهاية الأقدم بأمرين كانت تتلقاهما دائمًا: حقل timestamp في أعلى جسم الطلب يساوي وقت وقوع الحدث، وترويسة X-Idempotency-Key. يتوقف كلاهما عند التبديل إلى v1.
أجب بسرعة برمز 2xx. ينتظر VEXORS استجابتك حتى 10 ثوانٍ؛ ولا يُعدّ الإرسال ناجحًا إلا برمز 2xx. نفّذ معالجتك بعد أن تجيب، أو اجعلها قصيرة.
إعادة المحاولة. الإرسال الذي يفشل بخطأ شبكة أو مهلة أو 5xx أو 408 أو 429 يُعاد حتى 5 محاولات إجمالًا، بانتظار نحو 30 ثانية ثم دقيقة ودقيقتين وأربع دقائق (مع بعض التفاوت). أي 4xx آخر يُعدّ نهائيًا من المحاولة الأولى. قد تصل عمليات الإرسال بترتيب مختلف، ونادرًا أكثر من مرة — فتجاهل التكرار دائمًا بمعرّف الإرسال، وبمعرّف الحدث أيضًا إذا أردت تجاهل إعادة إرسال يدوية لحدث سبق أن عالجته. أي مشكلة من جهة VEXORS لا تُحتسب على نقطة النهاية لديك ولا تستهلك محاولة: ينتظر الإرسال ثم يُرسَل بعد معالجة المشكلة. وإذا استمرت المشكلة أكثر من ثلاثة أيام فقط يُدرَج الإرسال كفاشل، ويبقى بإمكانك إعادة إرساله. الإرسال الذي يفشل في كل محاولاته يُحفَظ 90 يومًا في قائمة عمليات إرسال نقطة النهاية، حيث يمكنك إعادة إرساله بعد إصلاح المستقبِل لديك.
التعطيل التلقائي. إذا فشلت 10 عمليات إرسال متتالية في كل محاولاتها، يعطّل VEXORS نقطة النهاية ويُخطر مديري شركتك داخل التطبيق وبالبريد. بعد إصلاح المستقبِل، أعد تفعيلها من الإعدادات ثم خطافات الويب (قائمة نقطة النهاية، تفعيل)؛ يُصفَّر عدّاد الفشل. كما يصفّره أي إرسال ناجح، ولا تُحتسب عمليات الإرسال الاختبارية فيه أبدًا. والإرسال الذي رُفض فقط لأنك جدّدت السرّ وهو في طريقه يُعاد بالسرّ الجديد ولا يُحتسب كذلك.
كتالوج الأحداث. القائمة الكاملة لرموز الأحداث، مصنّفة حسب المجال، هي نفسها المعروضة عند إضافة نقطة نهاية أو تعديلها؛ الرموز ثابتة وبصيغة المجال.الفعل (request.published، bid.shortlisted، award.issued، rating.submitted، opportunity.confirmed، service-offer.accepted، …).
مفاتيح API (Scale، متاحة الآن)
يمكن لأنظمتك استدعاء VEXORS مباشرةً، لا استقبال الأحداث المُرسَلة فقط. ينشئ مدير الشركة المفاتيح من الإعدادات ثم مفاتيح API: لكل مفتاح اسم والمجالات التي يحق له قراءتها أو تعديلها (الطلبات، والعروض، وعروض الخدمات، والكتالوج، وConnect، واستكشاف الشركات)، ويُعرض سرّه مرة واحدة. أرسل المفتاح في الترويسة x-vexors-api-key مع كل استدعاء لنقاط التكامل. يعمل المفتاح بصفة العضو الذي أنشأه وبصلاحياته، فلا يستطيع أبدًا أكثر مما يستطيعه ذلك العضو.
هذه الصفحة مقصورة على Scale. وعلى Grow وExplore تعرض دعوة للترقية بدلًا منها.
لا تسرد هذه الصفحة نقاط النهاية واحدةً واحدة بعد. تواصل معنا للحصول على مرجع نقاط النهاية للمجالات التي تحتاجها.
التخطيط لتكامل
إذا كنت تخطط لتكامل وترغب في مناقشته، فتواصل معنا وسنساعدك في تحديد نطاقه، سواء كان ذلك بخطافات الويب أو بمفاتيح API أو بكليهما.
تواصل معنا بشأن التكاملاتأدلة ذات صلة