تخطَّ إلى المحتوى

Webhook الصادرة

باستخدام webhook صادرة يرسل exoCortex طلب POST إلى نظامكم في كل مرة يقع فيها حدث اشتركتم فيه: إنشاء تذكرة، أو تغيّر حالة، أو SLA يقترب من الانتهاء. وبذلك يتفاعل نظامكم فورًا، دون الحاجة إلى الاستعلام من API على فترات.

  1. في exoCortex Core افتحوا Integrations → Webhooks. يلزم دور إداري.
  2. حدّدوا عنوان URL الذي سيستقبل الأحداث. يجب أن يكون https://.
  3. اختاروا الأحداث. تعني * جميع الأحداث.
  4. اختاروا الصيغة:
    • json: الصيغة القياسية لـ exoCortex، الموصوفة في هذه الصفحة؛
    • datadog، pagerduty، slack-block: الصيغة الأصلية لتلك الخدمات، وتُستخدم عندما يكون عنوان URL تابعًا لها.
  5. إذا كانت البوابة لديكم تتطلب ذلك، أضيفوا ترويسات ثابتة، مثل ترويسة مصادقة.
  6. احفظوا. إذا لم تحدّدوا سرّ توقيع، يُنشئ exoCortex سرًّا (whsec_…) ويعرضه مرة واحدة فقط: احفظوه، فأنتم بحاجة إليه للتحقق من التواقيع.

بإجراء الاختبار ترسلون إلى الاشتراك الحدث webhook.test وتتحققون من أن نظامكم يستقبله.

الحدث متى يصل
ticket.created أُنشئت تذكرة
ticket.comment_added أُضيف تعليق
ticket.field_changed تغيّر حقل
ticket.assignee_changed تغيّر المُسنَد إليه
ticket.status_changed تغيّرت الحالة
ticket.priority_changed تغيّرت الأولوية
ticket.sla_warning SLA على وشك الانتهاء
ticket.sla_breach انتهت مهلة SLA
ticket.resolved حُلّت التذكرة
ticket.closed أُغلقت التذكرة
ticket.reopened أُعيد فتح التذكرة
ticket.link_created أُنشئ ربط بين تذاكر
ticket.link_deleted أُزيل ربط بين تذاكر
routing.no_match لم تعثر أي قاعدة توجيه على قائمة انتظار للتذكرة
routing.manual_routed وجّه أحد الموظفين المختصين التذكرة يدويًا
webhook.test أرسلتم حدثًا تجريبيًا إلى الاشتراك

إضافةً إلى ذلك يمكنكم الاشتراك بالاسم في أحداث قواعد الإشعارات الخاصة بمستأجركم، مثل approval_requested.

قد يضيف exoCortex أنواعًا جديدة من الأحداث: يجب أن يتجاهل نظامكم الأنواع التي لا يعرفها.

لكل حدث بصيغة json البنية نفسها:

{
"id": "evt_8a1f4c2e9b7d4e3f8a6b5c4d3e2f1a00",
"type": "ticket.status_changed",
"created_at": "2026-11-01T09:31:47.120Z",
"api_version": "1",
"tenant_id": "ACME",
"data": {
"ticket": {
"key": "ACME-1042",
"summary": "Payment gateway returns HTTP 502",
"status": "In Progress",
"priority": "High",
"issuetype": "IT-INC",
"assignee": "jane.doe",
"created": "2026-11-01T09:14:05.000Z",
"updated": "2026-11-01T09:31:47.000Z"
},
"actor": "jane.doe",
"changes": { "from": "Open", "to": "In Progress" }
}
}
  • يعرّف id الحدث، وهو نفسه لجميع الاشتراكات التي تستقبله.
  • actor هو المستخدم الذي تسبّب في الحدث، أو system.
  • يتضمن changes تفاصيل الحدث: مثل الحالة السابقة والجديدة، أو نص التعليق.

حتى 30 يونيو 2027 يتضمن المتن أيضًا الحقول المُهمَلة event وtimestamp وsource، ونسخة من حقول data في المستوى الأول. يجب أن يقرأ أي نظام جديد الحقول الموصوفة أعلاه فقط.

الترويسة مثال المعنى
X-Exo-Timestamp 1793491200 لحظة التوقيع، بثواني Unix
X-Exo-Signature v1=5257a869… توقيع الحدث
X-Exo-Event ticket.created نوع الحدث، مطابق لـ type
X-Exo-Delivery dlv_0b9d… معرّف فريد لمحاولة التسليم

قبل الوثوق بحدث، تحققوا من توقيعه:

  1. اقرؤوا المتن كما وصل، دون تحليل JSON وإعادة كتابته: فذلك سيغيّر البايتات.
  2. ارفضوا الطلب إذا كانت X-Exo-Timestamp مفقودة أو تختلف بأكثر من 300 ثانية عن ساعتكم. وبذلك تمنعون الطلبات المكررة. حافظوا على مزامنة الساعة عبر NTP.
  3. احسبوا HMAC-SHA256 لـ timestamp + "." + body بسرّ الاشتراك، بالنظام الست عشري بأحرف صغيرة.
  4. قارنوا النتيجة، في زمن ثابت، بكل قيمة v1= في X-Exo-Signature. أثناء تدوير السر قد تحتوي الترويسة على أكثر من قيمة، مفصولة بفواصل.

مثال بلغة Node.js:

import { createHmac, timingSafeEqual } from 'node:crypto';
export function verifyExoWebhook(rawBody, headers, secret, toleranceSec = 300) {
const ts = headers['x-exo-timestamp'];
const sigHeader = headers['x-exo-signature'] || '';
if (!ts || Math.abs(Date.now() / 1000 - Number(ts)) > toleranceSec) return false;
const expected = Buffer.from(createHmac('sha256', secret).update(`${ts}.${rawBody}`).digest('hex'));
return sigHeader
.split(',')
.map((s) => s.trim())
.filter((s) => s.startsWith('v1='))
.some((s) => {
const got = Buffer.from(s.slice(3));
return got.length === expected.length && timingSafeEqual(got, expected);
});
}

مثال بلغة Python:

import hashlib, hmac, time
def verify_exo_webhook(raw_body: bytes, headers, secret: str, tolerance_sec: int = 300) -> bool:
ts = headers.get('X-Exo-Timestamp')
if not ts or abs(time.time() - int(ts)) > tolerance_sec:
return False
expected = hmac.new(secret.encode(), f"{ts}.".encode() + raw_body, hashlib.sha256).hexdigest()
sigs = [s.strip()[3:] for s in headers.get('X-Exo-Signature', '').split(',') if s.strip().startswith('v1=')]
return any(hmac.compare_digest(s, expected) for s in sigs)

الترويسة X-NH-Signature، التي توقّع المتن فقط دون طابع زمني، مُهمَلة وستُزال في 30 يونيو 2027.

الرد ومعالجة النسخ المكررة

Section titled “الرد ومعالجة النسخ المكررة”
  • ردّوا بأي رمز 2xx خلال 5 ثوانٍ. إذا كان عليكم تنفيذ عمل طويل، فضعوه في قائمة انتظار وردّوا فورًا.
  • التسليم مضمون مرة واحدة على الأقل: قد يصل الحدث نفسه أكثر من مرة. استخدموا id أو X-Exo-Delivery للتعرّف على النسخ المكررة، مثلًا بالاحتفاظ بالمعرّفات التي وردت خلال آخر 5 دقائق.
  • قد تصل الأحداث بترتيب مختلف عن ترتيب وقوعها: رتّبوها باستخدام created_at.