إرشادات خطوة بخطوة
من يمكنه القيام بذلك: يتطلب عرض Webhooks صلاحية integrations.view؛ ويتطلب الإنشاء والتعديل والحذف والتسليم اليدوي أيضًا integrations.manage. تنطبق قواعد الوحدة والاشتراك واستحقاق التكاملات.
قبل أن تبدأ
- مستقبِل HTTPS تتحكم فيه مؤسستك، باستخدام عنوانه النهائي بدلًا من إعادة توجيه.
- مخزن أسرار على الخادم، والتحقق من التوقيع باستخدام جسم الطلب الخام، وكشف دائم للتكرارات، ومراقبة. اقرأ القيود الحالية قبل الاعتماد على التسليم.
الخطوات
- اختر المؤسسة الصحيحة وافتح التكاملات ← Webhooks في /administration/integrations.
- اختر Webhook جديدًا، وأدخل اسمًا واضحًا ونقطة نهاية HTTPS، ثم اختر حدثًا مدعومًا واحدًا على الأقل.
- اختر نشط عندما يكون المستقبِل جاهزًا، ثم احفظ Webhook. انسخ سر التوقيع الذي يظهر إلى مخزن أسرار المستقبِل.
- استخدم حدثًا حقيقيًا مضبوطًا وغير ضار في مساحة عمل اختبارية لفحص التسليم التلقائي. لا يختبر الحفظ الاتصال، ولا تتضمن الواجهة الحالية زرًا مخصصًا لإرسال اختبار.
النتيجة المتوقعة
يمكن للمستقبِل التحقق من إشعارات الأحداث المشترك فيها وقبولها، وفق حدود التسليم ومصادر الأحداث المدعومة.
الإجراء التالي
طبّق التحقق من التوقيع ومنع تكرار التنفيذ، وراقب إخفاقات التسليم، وطابق البيانات الحرجة بشكل مستقل عن الإشعارات.
الوصول إلى نقطة النهاية وإعدادها
هذه إشعارات صادرة للعملاء، منفصلة عن مزامنة المزوّد الواردة وإشعارات مزوّد الفوترة. لا يمنح مفتاح API صلاحية إدارة Webhook. يتحقق الحفظ أو التسليم اليدوي من استحقاق التكاملات، إلى جانب الوصول العادي للمؤسسة.
استخدم مستقبِلًا تملكه وتخوّله بتلقي بيانات الأحداث. لا تضع بيانات اعتماد في عنوانه ولا تستخدم عناوين الاسترجاع المحلي أو البنية التحتية الخاصة. يلزم HTTPS، لكن الحفظ لا يثبت الملكية أو إمكانية الوصول أو أمان الشبكة أو سلوك إعادة التوجيه.
اشتراكات الأحداث المدعومة
استخدم أسماء الأحداث هذه كما هي تمامًا. لا تُدعم أحرف البدل. قد ينتج عن إكمال عنصر عمل مجددًا بعد إعادة فتحه حدث أعمال جديد، وليس تكرارًا للحدث السابق.
| اسم الحدث | متى يصدر تلقائيًا |
|---|---|
ProjectCreated | يُنشأ مشروع. |
ProjectUpdated | لا يصدر حاليًا عند التعديلات العادية على المشاريع. |
WorkItemCompleted | تتغير حالة عنصر العمل إلى منجز. |
ResourceAllocated | يُنشأ تخصيص موارد. |
TimesheetApproved | تتغير حالة سجل ساعات العمل إلى معتمد. |
RiskCreated | يُنشأ خطر للمشروع. |
ChangeRequestApproved | تتغير حالة طلب التغيير إلى معتمد. |
ترويسات طلب التسليم
ترسل Allocyn طلب POST مع Content-Type: application/json; charset=utf-8. لا تتأثر أسماء الترويسات بحالة الأحرف. يصادق التوقيع على الطابع الزمني والحمولة، وليس على ترويستي الحدث ومعرّف التسليم بشكل منفصل؛ استخدم الحقل type من الجسم الذي تم التحقق منه لتوجيه الحدث بشكل موثوق.
| الترويسة | المعنى |
|---|---|
X-Allocyn-Signature | sha256= متبوعًا بـ64 محرفًا سداسيًا عشريًا صغيرًا لقيمة HMAC. |
X-Allocyn-Timestamp | وقت Unix عشري بالثواني لهذه المحاولة. |
X-Allocyn-Event | اسم الحدث؛ تحقّق من جسم الطلب قبل توجيه العمل. |
X-Allocyn-Delivery-Id | معرّف التسليم المحفوظ عبر إعادة المحاولات؛ ولا يُوقّع بشكل مستقل. |
حمولة الحدث التلقائي
نُسّقت هذه الحمولة الافتراضية لتسهيل قراءتها. يحدد id حدث الأعمال لا عملية التسليم؛ وoccurredAtUtc هو وقت الحدث لا وقت إعادة المحاولة. لا يتضمن الغلاف معرّف المؤسسة أو منفّذ الإجراء أو إصدار المخطط. اربط نقطة النهاية والسر بمؤسستك في نظام الاستقبال.
يحتوي كائن data على تفاصيل خاصة بالحدث، لا لقطة كاملة للكيان. قد تكون Changes فارغة، وقد تكون oldValue وnewValue نصوصًا أو null حتى للقيم التي تبدو رقمية. تحقّق مما يحتاج إليه سير عملك وتقبّل الحقول الإضافية. تدعم قراءات المتابعة عبر API العامة مجموعاتها الأربع الموثقة فقط.
{
"id": "11111111-1111-7111-8111-111111111111",
"type": "WorkItemCompleted",
"occurredAtUtc": "2026-09-07T10:30:00Z",
"entityType": "WorkItem",
"entityId": "22222222-2222-7222-8222-222222222222",
"data": {
"changes": {
"Status": { "oldValue": "InProgress", "newValue": "Done" }
}
}
}التحقق من التوقيع قبل تحليل JSON
احتفظ ببايتات جسم الطلب المستلَم كما هي تمامًا قبل تحليل JSON. لا تعِد تنسيقها أو تسلسلها أو توحيد مسافاتها أو تغيير تسلسلات الإفلات. استخدم نص سر التوقيع حرفيًا كمفتاح UTF-8: رغم أنه يبدو كـ Base64، فلا تفك ترميزه أو تقتطع منه أو تستبدله بمفتاح API.
يتحقق مثال Node.js من التوقيعات فقط. يجب على المستقبِل فرض حد لحجم الجسم، ورفض الترويسات المكررة أو الملتبسة، والحصول على السر بأمان، وتمرير Buffer الخام. نافذة الطابع الزمني البالغة خمس دقائق سياسة موصى بها للمستقبِل، وليست ضمانًا من المرسِل؛ تحقّق من طابع المحاولة الزمني، لا وقت الحدث الأقدم.
ارفض الطلب عند فشل التحقق. وبعد نجاحه، حلّل الجسم وتحقّق منه ومن نوع الحدث المقبول، ثم احفظه أو أدرجه في قائمة انتظار دائمة قبل تأكيد الاستلام. نجاح التحقق وحده لا يمنع إعادة الإرسال ولا يتحقق من صحة بيانات التطبيق.
message = UTF8(timestampText + ".") followed by the raw payload bytes
key = UTF8(signingSecretText)
digest = HMAC-SHA256(key, message)
signatureHeader = "sha256=" + lowercaseHex(digest)import { createHmac, timingSafeEqual } from 'node:crypto';
export function verifyAllocynWebhook(options) {
if (!options || typeof options !== 'object') return false;
const {
rawBody, signature, timestamp, secret,
nowSeconds = Math.floor(Date.now() / 1000),
toleranceSeconds = 300,
} = options;
if (!Buffer.isBuffer(rawBody) || typeof secret !== 'string' || !secret.length) return false;
if (typeof signature !== 'string' || signature.length !== 71 ||
!/^sha256=[0-9a-f]{64}$/.test(signature)) return false;
if (typeof timestamp !== 'string' || !timestamp.length || timestamp.length > 16) return false;
const seconds = Number(timestamp);
if (!Number.isSafeInteger(seconds) || seconds < 0 || String(seconds) !== timestamp ||
!Number.isSafeInteger(nowSeconds) || nowSeconds < 0 ||
!Number.isSafeInteger(toleranceSeconds) || toleranceSeconds < 0 ||
Math.abs(nowSeconds - seconds) > toleranceSeconds) return false;
const expected = createHmac('sha256', Buffer.from(secret, 'utf8'))
.update(Buffer.from(timestamp + '.', 'utf8'))
.update(rawBody)
.digest();
const supplied = Buffer.from(signature.slice('sha256='.length), 'hex');
return supplied.length === expected.length && timingSafeEqual(supplied, expected);
}التكرارات وتأكيدات الاستلام
أعد الرمز 2xx بعد قبول الحدث وحفظه بشكل دائم، ويفضّل قبل مهلة المرسِل البالغة 15 ثانية بوقت كافٍ، ونفّذ العمل الأطول بصورة غير متزامنة. أعد إخفاقًا إذا تعذّر قبول الحدث بأمان. وللتكرار المقبول سابقًا، أعد نجاحًا دون تكرار آثاره الجانبية.
للأحداث التلقائية، طبّق منعًا دائمًا وذريًا للتكرار باستخدام نقطة النهاية المُعدّة مع id من جسم الطلب الذي تم التحقق منه. لا تعتمد على ترويسة معرّف التسليم غير الموقعة وحدها للتحقق من إعادة الإرسال. قد تخلو الإرسالات اليدوية من معرّف حدث في الجسم، وتحتاج إلى مخطط موثّق خاص بها ومفتاح يمنع تكرار التنفيذ. لا يوجد ضمان للتنفيذ مرة واحدة بالضبط أو للترتيب الصارم.
إعادة المحاولة والاسترداد
أوقات إعادة المحاولة فترات انتظار للأهلية، وليست جداول دقيقة. قد تؤدي الإخفاقات عبر عدة عمليات تسليم إلى تعطيل نقطة النهاية قبل أن تبلغ عملية واحدة خمس محاولات. تحتفظ الإعادات بمعرّف التسليم وتفاصيل الجسم الأصلي، لكنها تتلقى طابعًا زمنيًا وتوقيعًا جديدين للمحاولة.
عالج مشكلات المستقبِل أو الاتصال أو الشهادة أو السر، ثم عدّل Webhook وفعّل نشط مجددًا لإعادة تعيين الإخفاقات المتتالية. يمكن استئناف عمليات التسليم غير المكتملة المؤهلة؛ ولا تعيد إعادة التفعيل تعيين عمليات التسليم التي بلغت خمس محاولات، ولا توجد عملية لإعادة تشغيل التسليم المستنفد أو إعادة تعيينه.
| السلوك | التنفيذ الحالي |
|---|---|
| نجاح | أي استجابة HTTP برمز 2xx. |
| إخفاق | الاستجابات غير 2xx؛ وتُسجّل أخطاء الشبكة وانتهاء المهلة بالرمز 503. |
| مهلة HTTP | 15 ثانية. |
| الحد الأقصى للمحاولات | خمس محاولات إجمالًا: محاولة أولى وما يصل إلى أربع إعادات. |
| فواصل إعادة المحاولة | تصبح المحاولة مؤهلة بعد 1 ثم 2 ثم 4 ثم 8 دقائق من المحاولة السابقة؛ وتتحقق عمليات الخلفية كل دقيقة تقريبًا. |
| التعطيل التلقائي | خمسة إخفاقات متتالية لنقطة النهاية؛ وأي نجاح يعيد العدد إلى الصفر. |
تغيير نقطة نهاية أو إيقافها مؤقتًا أو حذفها
عدّل Webhook واختر استبدال سر التوقيع لإنشاء سر جديد، وانسخه عند ظهوره. الحفظ دون استبدال يحتفظ بالسر الحالي. لا توجد فترة سماح للسر القديم: تستخدم المحاولات اللاحقة، بما فيها الإعادات المنتظرة، العنوان والسر الحاليين. نسّق التغييرات مع المستقبِل؛ فقد يستمر طلب قيد التنفيذ باستخدام القيم السابقة.
لا يمسح الإيقاف المؤقت الطلبات المتراكمة. يزيل الحذف اشتراكات نقطة النهاية وسجل تسليمها المخزّن، وليس حالتها النشطة فقط. احفظ معلومات استكشاف المشكلات اللازمة بأمان قبل الحذف، أو أوقفها مؤقتًا للاحتفاظ بالسجل. لا يمكن للحذف سحب الأحداث المستلَمة أو ضمان إلغاء إرسال قيد التنفيذ.
مرجع إدارة Webhook
استخدم وصول المؤسسة المعتاد الموثّق، وليس مفتاح API. أضف قبل هذه المسارات /api/organization/integration-operations وأي مسار أساسي للنشر. يتطلب GET صلاحية integrations.view؛ وتتطلب العمليات الأخرى أيضًا integrations.manage. استخدم أحدث قيمة version المعتمة بترميز Base64 للتعديل والحذف؛ وأعد التحميل بعد تعارضات 409. تعيد الإعدادات غير الصالحة 400، وقد تعيد إخفاقات الوصول 401 أو 403.
تتضمن ملخصات نقاط النهاية id وname وurl وenabled وconsecutiveFailures وevents وdeliveries وversion. وتتضمن ملخصات التسليم id وbusinessEventId وeventType وattemptCount وstatusCode وsucceeded وerrorSummary وattemptedAtUtc. تعرض الملخصات أحدث محاولة، لا صفًا لكل محاولة. تتضمن القوائم ما يصل إلى 200 عملية تسليم حديثة على مستوى المؤسسة؛ وتعرض الواجهة عدد عمليات التسليم الحديثة.
يتطلب التسليم اليدوي نقطة نهاية نشطة مشتركة في الحدث. يجب أن يكون payloadJson نصًا يحتوي على JSON صالح؛ ويُرسل مباشرةً دون الغلاف التلقائي ولا يحتوي على businessEventId. لا يغيّر أي سجل أعمال. افحص succeeded في الملخص المعاد حتى عندما يعيد طلب الإدارة الرمز 200، واقرأ قيد التسلسل أدناه قبل استخدامه.
| الطريقة والمسار | الطلب | الاستجابة |
|---|---|---|
GET /webhooks | — | 200: WebhookEndpointSummary[] |
POST /webhooks | {name, url, enabled, rotateSecret, events, version} | 200: {endpoint, signingSecret} |
PUT /webhooks/{id} | {name, url, enabled, rotateSecret, events, version} | 200: {endpoint, signingSecret} |
POST /webhooks/{id}/deliver | {eventType, payloadJson} | 200: WebhookDeliverySummary |
DELETE /webhooks/{id} | {version} | 204 |
{
"name": "Delivery receiver",
"url": "https://example.com/hooks/allocyn",
"enabled": false,
"rotateSecret": false,
"events": ["WorkItemCompleted", "TimesheetApproved"],
"version": null
}القيود الحالية لـ Webhook
للدعم، قدّم اسم نقطة النهاية ووقت المحاولة بتوقيت UTC ومعرّف التسليم ونوع الحدث والحالة أو ملخص الخطأ. لا تشارك أسرار التوقيع أو الحمولات الحساسة.
- يمكن اختيار ProjectUpdated، لكن التعديلات العادية على المشاريع تُصدر أسماء أحداث داخلية مختلفة غير مدعومة كاشتراكات Webhook. لا تعتمد عليه لإشعارات تعديل المشاريع.
- في JSON اليدوي، يوقّع المرسِل النص المقدّم ثم يحلله ويعيد تسلسله للإرسال. قد تغيّر المسافات والنصوص غير ASCII وتسلسلات الإفلات البايتات. ارفض عدم تطابق توقيع الجسم الخام وأبلغ عنه؛ لا تعدّل JSON الوارد لتوحيده ولا تعطّل التحقق. تتطلب موثوقية الحمولات اليدوية الاعتباطية تصحيحًا لدى المرسِل.
- يفحص النشر التلقائي الأيام السبعة السابقة، دون الرجوع إلى ما قبل إنشاء نقطة النهاية. ويقرأ أقدم 1,000 حدث قبل تصفية الاشتراكات المدعومة. هذا ليس تصديرًا كاملًا للسجل ولا ضمانًا لمعالجة كل الأحداث المتراكمة؛ فقد توجد أحداث خارج تلك الدفعة في المؤسسات ذات الأحجام الكبيرة.
- تفحص كل دورة لإعادة المحاولة أقدم 50 عملية تسليم فاشلة غير مكتملة قبل التحقق من مواعيد الاستحقاق ونقاط النهاية المفعّلة. قد تؤخر التراكمات الأعمال المؤهلة. وقد تترك فترات التعطل أو استنفاد المحاولات أو حدود الحجم أو الإيقاف المؤقت فجوات: احتفظ بمطابقة مستقلة وتنبيهات للأعمال الحرجة.
مشكلات شائعة
- يمكن اختيار ProjectUpdated، لكنه لا يصدر تلقائيًا حاليًا عند التعديلات العادية على المشاريع.
- قد يواجه JSON المقدّم يدويًا عدم تطابق بين التوقيع وتسلسل الجسم. ارفض حالات عدم التطابق؛ ولا تتجاوز التحقق مطلقًا لتمريرها.
- يؤدي حذف نقطة النهاية أيضًا إلى إزالة سجل تسليمها المخزّن. أوقفها مؤقتًا بدلًا من ذلك إذا كان يجب الاحتفاظ بالسجل.