سجل تغييرات الواجهة البرمجية

للمسؤولين

نسرد هنا كل تغيير على الواجهة البرمجية العامة (/api/v1) وأحداث خطافات الويب، الأحدث أولاً. التغييرات الإضافية تُضاف إلى v1، أما التغييرات الكاسرة فتنتقل إلى /api/v2 مع فترة إيقاف تدريجي.

2026-10-25

  • إعادة محاولة خطافات الويب وإكمال المهام المجمّعة مرة واحدة يوميًا في الخدمة المستضافة: تشغّل الخدمة المستضافة حاليًا مُرسِلها العامل في الخلفية مرة واحدة يوميًا، في الساعة 03:00 بالتوقيت العالمي (UTC). وما زالت المحاولة الأولى لخطاف الويب والدقيقة الأولى من عمل المهمة المجمّعة تُنفَّذان فورًا. أما إعادة المحاولة، وما تبقى من المهام المجمّعة الأطول، والتسليمات المعلّقة بإيقاف مؤقت، والحذف بعد مدة الحفظ، فتُنفَّذ في التشغيل اليومي التالي، وتُعطَّل نقطة الاستقبال التي تفشل 3 أيام عند محاولتها الفاشلة التالية (في الخدمة المستضافة عادةً في التشغيل اليومي التالي). وتُرسَل إعادة المحاولة في أول تشغيل يومي بعد انقضاء تأخيرها، وتمتد المحاولات العشر 8 أيام أو أكثر (بدلًا من نحو 3 أيام و20 ساعة). ولم يتغير جدول الفواصل نفسه، ويحتفظ النشر المستضاف ذاتيًا الذي يشغّل المُرسِل كل دقيقة بالتوقيت الموثّق. انظر متى تنفذ إعادة المحاولة.

  • تغيير كاسر للمفاتيح التي لا تملك structure.functional.salary: الأجر مخفي في كل قراءة. تعيد الوظائف (payScaleId وpayGradeId وpayStepNumber وbaseSalary وpayPeriod) والأوصاف الوظيفية (payScaleId وpayGradeId وsalaryMin وsalaryMax) هذه الحقول الآن بالقيمة null للمفاتيح التي لا تملك structure.functional.salary: في القوائم والقراءة المفردة، وفي استجابات POST وPATCH والأرشفة وإلغائها وPUT بالمعرّف الخارجي، وفي حمولات خطافات الويب position.* وjob_description.* (ومنها إعادة الإرسال). وتُستبعد حقول الأجر أيضاً من data.changedFields، ولم يعد يُرسل إلى نقاط الاستقبال تلك position.updated أو job_description.updated لم يغيّر إلا الأجر (مثل إعادة التسعير عند تعديل مرتبة رواتب). وفي سجل التدقيق (GET /api/v1/audit-events) وaudit.recorded تُستبعد مفاتيح الأجر من metadata.changes في صفوف الوظائف والأوصاف الوظيفية، ولا تحمل صفوف سلالم الرواتب metadata.changes (ولا metadata.rank للمرتبة)، وتُستبعد معرّفات الأجر ومبالغه المحفوظة في المستوى الأعلى من metadata (مثلاً عند إنشاء وظيفة أو شاغر) وmetadata.gradeCode. سابقاً كان أي مفتاح يقرأ الوظائف أو الأوصاف الوظيفية يرى أجرها، ولم تكن صلاحية الرواتب مطلوبة إلا لكتابته. ولا يتغيّر شيء للمفاتيح التي تملك structure.functional.salary ولا لمفاتيح مستوى النشر. أضف structure.functional.salary إلى التكامل الذي يحتاج الأجر.

  • إعادة ربط معرّف خارجي بعد حذف سجله: ربط معرّف خارجي ما زال يحمله سجل لم يعد موجوداً (مثل وصف وظيفي أو وظيفة أو وحدة تنظيمية أو شخص حُذف من التطبيق) يُزيل الآن الربط القديم ويربط المعرّف بالسجل الجديد، بدلاً من إعادة 409 conflict. ويشمل ذلك externalRef في POST وPATCH /api/v1/job-descriptions. أما المعرّف الذي يحمله سجل ما زال موجوداً فما زال يعيد 409 conflict.

  • نسخ المجموعات ومسودات خطة القوى العاملة وتعديل مراتب الرواتب ترسل أحداث الوظائف والأوصاف الوظيفية: نسخ نسخة مجموعة وظيفية إلى صفة أخرى مع أوصافها الوظيفية ووظائفها يرسل الآن job_description.created وposition.created لكل نسخة (أو <resource>.bulk_changed إذا تجاوزت 50)، إلى جانب job_family.updated الذي كان يُرسل؛ ولا يُنسخ إلا ما يخص مساحة عملك من أوصاف ووظائف. وتطبيق توصية نقص في الأعداد من خطة القوى العاملة يرسل الآن position.created للوظيفة المسودة التي ينشئها. وتعديل مبالغ مرتبة رواتب أو عدد درجاتها يرسل الآن position.updated (changedFields: ["payStepNumber", "baseSalary"]) لكل وظيفة يعيد تسعيرها وjob_description.updated (changedFields: ["salaryMin", "salaryMax"]) لكل وصف وظيفي يتغيّر نطاقه (أو <resource>.bulk_changed إذا تجاوزت 50)، إلى جانب pay_scale.updated. سابقاً كانت هذه الصفوف تتغيّر دون أي حدث. ولم يعد تعديل مرتبة لا يغيّر الأجر (الاسم أو الرمز أو الترتيب فقط) يحرّك updatedAt لتلك الوظائف والأوصاف.

  • أرشفة وصف وظيفي أو حذفه ترسل opening.closed: عند أرشفة وصف وظيفي (POST /api/v1/job-descriptions/{id}/archive أو من التطبيق) أو حذفه من الهيكل التنظيمي، يرسل الآن كل شاغر مفتوح يُغلق بسببه opening.closed (أو opening.bulk_changed واحداً إذا تجاوزت 50)، إلى جانب position.updated الذي كان يُرسل لكل وظيفة. سابقاً كانت هذه الشواغر تصبح CLOSED دون أي حدث. ويُضبط الآن closedAt أيضاً للشاغر الذي يُغلق بحذف وصف وظيفي.

  • GET /api/v1/ping صار في مستند OpenAPI: يسرد GET /api/v1/openapi.json ومرجع الواجهة البرمجية الآن GET /api/v1/ping (أي مفتاح صالح، دون صلاحية) مع استجابته: ok، والتكامل الذي يتبعه المفتاح في actor (kind بقيمة integration أو platform)، وworkspaceId (null لمفتاح على مستوى النشر). لم تتغير نقطة النهاية نفسها.

  • تغييرات المستخدمين من لوحة إدارة المنصة ترسل أحداث user.*: عندما يغيّر فريق دعم جديرون دور أحد مستخدمي مساحة عملك من الموظفين، أو يعطّل حسابه أو يعيد تفعيله، أو يزيله من لوحة إدارة المنصة، تتلقى نقاط الاستقبال في مساحة عملك الآن user.updated (changedFields: ["role"]، أو ["status", "disabledAt"] للتعطيل وإعادة التفعيل) أو user.deleted، كما في التغيير نفسه من إعدادات المستخدمين والأدوار (سابقًا كان audit.recorded وحده يبلّغ عنها). ويظهر المستخدم المُزال أيضًا في سجل المحذوفات.

  • حذف نسخة واحدة من مجموعة وظيفية يحرّك updatedAt الخاص بها: عندما تحذف مساحة عملك نسخة واحدة من مجموعة وظيفية لها نسخ أخرى (مثلاً نسخة لتكليف واحد)، يتغيّر الآن updatedAt للنسخ المتبقية، فيتقدّم updatedAt الخاص بالمجموعة ويلتقطها updatedSince مع حدث job_family.updated (changedFields: ["instances"]) الذي كان يُرسل أصلاً. سابقاً كان updatedAt الخاص بالمجموعة قد يرجع إلى الوراء (إلى ختم النسخ المتبقية الأقدم) فيفوت التغييرُ updatedSince.

  • قائمة عناوين IP تقرأ عنوان عميل لا يمكن تزويره: صارت قائمة عناوين IP لمفاتيح الواجهة البرمجية وحدود المعدّل وعنوان IP في سجلات التدقيق تستخدم قاعدة واحدة لعنوان العميل. لا يتغيّر شيء في سحابة جديرون (تضبط حافة الاستضافة عنوان العميل). أما النشر الذاتي فيضبط TRUSTED_PROXY (عدد الوكلاء العكسيين، أو عناوين IP أو نطاقات CIDR الخاصة بهم) فيُقرأ عنوان العميل من يمين X-Forwarded-For، فلم تعد القيمة التي يضعها العميل في هذه الترويسة تتجاوز القائمة. ودون TRUSTED_PROXY لا يثق النشر الذاتي بأي ترويسة تمرير: تتلقى مفاتيح التكامل الذي له قائمة عناوين 403 forbidden. راجع قائمة عناوين IP المسموحة.

  • أخطاء التعيين ونقل المرحلة لم تعد تكشف نصوصاً داخلية: إذا جرى تعيينان للمرشح نفسه في الوقت نفسه (عبر طلبين له مثلاً)، يعيد POST /api/v1/applications/{id}/hire الآن للتعيين الذي لم ينجح 409 conflict مع details.reason بقيمة concurrent_hire، بدلاً من 422 validation_failed يحمل رسالة من قاعدة البيانات. والإخفاق غير المتوقع أثناء /hire أو /stage أو /reject يعيد الآن 500 internal (يمكن إعادة المحاولة بتأخير متزايد) بدلاً من 422 validation_failed مع نص الخطأ الداخلي. أما الأسباب الموثّقة (position_full وnot_a_candidate وopening_not_open وalready_hired وalready_rejected) فلم تتغير.

  • تغيير كاسر: إنشاء مرشحين ببيانات تواصل يحتاج recruitment.candidate.contact: أصبح POST /api/v1/candidates الذي يرسل email أو phone أو nationalId بقيمة غير فارغة يعيد 403 forbidden (details.reason بقيمة candidate_contact_required وdetails.required بقيمة ["recruitment.candidate.contact"]) للمفتاح الذي لا يملك صلاحية التواصل، قبل أي فحص للتكرار، فلم يعد 409 conflict (مع details.reason بقيمة duplicate_person) يكشف لهذا المفتاح وجود شخص بذلك البريد أو رقم الهوية. وفي POST /api/v1/candidates/bulk يفشل الصف الذي يحمل بيانات تواصل بالطريقة نفسها، وذلك الصف وحده، سواء أنشأ المرشح أم عدّله. وما زال بإمكان المفاتيح التي لا تملك صلاحية التواصل إنشاء مرشحين دون بيانات تواصل (احذف الحقول أو أرسل null)، وما زال PATCH يتجاهل حقول التواصل دونها. امنح تكاملات الترحيل وأنظمة ERP صلاحية التواصل لاستيراد بيانات التواصل.

  • اختيار قطاع فرعي أو إلغاء اختياره يحدّث المجموعات المندرجة تحته: عندما تختار مساحة عملك قطاعات فرعية أو تلغي اختيارها (أثناء التهيئة أو في صفحة الهيكل الوظيفي)، فإن كل مجموعة وظيفية تبقى فيها وتندرج تحت أحد تلك القطاعات الفرعية يتغيّر الآن updatedAt لنسخها (فيلتقطها updatedSince) ويُرسل لها job_family.updated (changedFields: ["instances"])، أو حدث واحد job_family.bulk_changed إذا تجاوز عددها 50، لأن subSectors الخاصة بها تغيّرت. سابقاً كانت المجموعات المحذوفة مع قطاع فرعي أُلغي اختياره هي وحدها التي ترسل حدثاً.

  • تغيير كاسر: تنزيل السيرة الذاتية وخطاب العرض يحتاج recruitment.candidate.contact: أصبح GET /api/v1/candidates/{id}/cv وGET /api/v1/offers/{id}/file يحتاجان صلاحية التواصل أيضاً، لأن السيرة الذاتية تعرض بريد المرشح وهاتفه، وقد يطبع خطاب العرض رقم هويته. والمفتاح الذي يملك recruitment.view دون recruitment.candidate.contact يتلقى الآن 403 forbidden (details.reason بقيمة candidate_contact_required، وdetails.required بقيمة ["recruitment.candidate.contact"]) بدلاً من الملف. أضف الصلاحية إلى التكامل لمواصلة التنزيل. ولم يتغيّر hasCv وhasFile، ولا تتأثر المفاتيح على مستوى النشر.

  • الطلبات والعروض والمقابلات والشواغر المحذوفة تظهر في سجل المحذوفات: عند حذف شخص في التطبيق (من صفحة الأشخاص، أو حين يحذف المرشح حسابه بنفسه) تُحذف نهائياً طلباته والعروض المقدَّمة عليها ومقابلاته، وحذف شخص مع الحذف المتتابع في محرر البيانات يحذف طلباته ومقابلاته كذلك، وحذف وظيفة بالطريقة نفسها يحذف شواغرها (ولا يمكن حذف وظيفة في شواغرها طلبات من محرر البيانات). وصار كل صف محذوف يترك أثر حذف في GET /api/v1/deleted (resource بقيمة application أو offer أو interview أو opening) مع externalRefs التي كانت له، وتُزال روابطه، فإعادة إرسال المعرّف الخارجي نفسه لاحقاً تُنشئ سجلاً جديداً. سابقاً كانت هذه الصفوف تختفي دون أثر حذف وتبقى روابطها. ويُرسل حفظ محرر البيانات الذي يحذف وظيفة حذفاً متتابعاً الحدث opening.bulk_changed (نوع حدث جديد)؛ أعد قراءة الشواغر باستخدام updatedSince وسجل المحذوفات.

  • يُحجز Idempotency-Key غير المكتمل 90 ثانية: المفتاح الذي لم يكتمل طلبه الأول يُحرَّر الآن بعد 90 ثانية من أول استخدام له (كانت 60 ثانية، أي أطول مدة قد يعمل فيها الطلب، فكان يمكن لإعادة محاولة أن تأخذ مفتاح طلب بطيء ما زال يعمل فيُنفَّذ مرتين). وحتى ذلك الحين تعيد إعادة المحاولة بالمفتاح نفسه 409 idempotency_conflict. ولم يعد الطلب الذي حُرِّر مفتاحه بهذه الطريقة يكتب فوق الاستجابة التي حفظتها إعادة المحاولة.

  • حذف وظيفة يحدّث الموظفين السابقين المرتبطين بها: عند حذف وظيفة (عبر DELETE /api/v1/positions/{id} أو من التطبيق) تصبح currentPositionId بقيمة null لدى الموظفين السابقين الذين كانت وظيفتهم، ويتغيّر updatedAt لهم (فيلتقطهم updatedSince)، ويُرسل لكل منهم employee.updated (changedFields: ["currentPositionId"])، أو حدث واحد employee.bulk_changed إذا تجاوز عددهم 50. سابقاً كان الربط يُمسح دون إشعار. وما زال حذف وظيفة فيها موظفون حاليون مرفوضاً (409 has_people).

  • حماية الطلبات المعيَّنة والمرفوضة: تعيد POST /api/v1/applications/{id}/stage و/reject و/hire على طلب في مرحلة HIRED القيمة 409 conflict مع details.reason بقيمة already_hired (كان يمكن سابقاً إعادة نقل الطلب المعيَّن أو رفضه). ويعيد /hire على طلب REJECTED القيمة 409 conflict (already_rejected)؛ أعده أولاً إلى مرحلة نشطة عبر /stage. وتعيين طلب معيَّن بالفعل يُبلغ الآن already_hired بدلاً من not_a_candidate. ورفض طلب مرفوض بالفعل ما زال لا يغيّر شيئاً. ويتبع التطبيق القواعد نفسها.

  • طلب PATCH للوصف الوظيفي يحتفظ بنطاق الراتب: لم يعد PATCH /api/v1/job-descriptions/{id} دون payScale وpayGrade يمسح salaryMin وsalaryMax لوصف وظيفي بلا مرتبة أجر (نطاق حُدّد في محرر البيانات)، ولا يعيد قراءة نطاق وصف له مرتبة. وما زال إرسال payScale أو payGrade (مع structure.functional.salary) يحدد النطاق من المرتبة، وما زالت القيمة payScale: null تمسحه.

  • التصفح مع updatedSince لم يعد يتخطى صفوفاً أو يفشل عند حذف صف: أصبح nextCursor يحمل موضع آخر صف (updatedAt وid؛ وoccurredAt وid في GET /api/v1/audit-events)، فلم يعد تعديل صف بين طلبَي صفحتين يجعل الصفحة التالية تتخطى صفوفاً، ولم يعد حذف آخر صف يجعل الصفحة التالية تفشل بـ422 validation_failed. وتبقى المؤشرات معتمة، والمؤشرات الصادرة قبل هذا التغيير تعمل كما كانت. والمؤشر المشوَّه ما زال يُعيد 422 validation_failed. ولا تغيير على القوائم دون updatedSince.

  • حدث interview.completed واحد لكل مقابلة: أول بطاقة تقييم من أعضاء اللجنة تجعل المقابلة COMPLETED وتُرسل interview.completed مرة واحدة؛ ويظل بإمكان بقية اللجنة إرسال بطاقاتهم (أو تعديلها)، وكل منها يُرسل interview.updated مع changedFields: ["evaluation"] (سابقاً كانت كل بطاقة تُرسل interview.completed وتبقى الحالة CONFIRMED). وأصبحت كل بطاقة تقييم تُحدّث updatedAt للمقابلة. ويُعيد PATCH /api/v1/interviews/{id} على مقابلة COMPLETED الخطأ 409 conflict (interview_closed) لطلب مواعيد جديدة وللإلغاء معاً.

  • أرشفة وظيفة لها شاغر مفتوح تتطلب recruitment.hiring.open: يغلق POST /api/v1/positions/{id}/archive الشاغر المفتوح للوظيفة، لذا أصبح يتطلب أيضاً recruitment.hiring.open عند وجود شاغر مفتوح، كما في التطبيق؛ ودونها يُعاد 403 forbidden (مع details.reason بقيمة hiring_permission_required وdetails.permission بقيمة recruitment.hiring.open) ولا يتغيّر شيء. أما الوظيفة التي ليس لها شاغر مفتوح فتُؤرشف بـstructure.org.act وحدها كما كانت.

  • التحقق من مساحة العمل عند بث الملفات الخاصة: لا يبث GET /api/v1/candidates/{id}/cv وGET /api/v1/offers/{id}/file الملف المخزّن إلا إذا كان تابعاً لمساحة عمل السجل؛ وأي ملف آخر يُعيد 404 not_found كالملف غير الموجود. ولا يقبل محرر البيانات في خلية السيرة الذاتية أو خطاب العرض أو الصورة لشخص إلا رابط http(s) أو قيمة فارغة؛ وأي قيمة أخرى، كمرجع ملف خاص، تُفشل الحفظ بخطأ تحقق. وتُرفق الملفات بالرفع فقط.

  • إبلاغ مالكي لوحة الإدارة بالبريد عند التعطيل التلقائي لنقطة استقبال على مستوى النشر: عندما تُعطَّل نقطة استقبال خطاف ويب في لوحة إدارة المنصة بعد فشل متواصل مدة 3 أيام، يتلقى كل مالك في لوحة الإدارة رسالة بريد واحدة تذكر التكامل ونطاق نقطة الاستقبال ومكان إعادة تفعيلها (لوحة إدارة المنصة ← التكاملات). ولا تغيير على نقاط استقبال مساحة العمل (يتلقى مسؤولو مساحة العمل إشعارًا داخل التطبيق).

  • التحقق من أنواع أحداث نقاط الاستقبال على مستوى النشر مقابل الكتالوج: أصبح إنشاء نقطة استقبال لخطافات الويب أو تعديلها في لوحة إدارة المنصة يقبل فقط * أو audit.recorded أو نوعًا من كتالوج الأحداث (<resource>.<verb>، مثل employee.updated)، كما هو الحال في نقاط استقبال مساحات العمل؛ ويُرفض أي نوع آخر بـ422 unknown_event_type (وتسرد details.invalid الأنواع المرفوضة) ولا يُحفظ شيء. ويظل بإمكان تكامل القراءة على مستوى النشر اختيار audit.recorded دائمًا. وكما في نقاط استقبال مساحات العمل، تؤدي إزالة أنواع أحداث من نقطة استقبال إلى وسم تسليماتها المعلّقة من تلك الأنواع بـFAILED (scope changed).

  • إعادة تسمية مهارة خاصة بمساحة العمل أو حذفها تحدّث المجموعات الوظيفية والأوصاف الوظيفية التي تسردها: عندما تعيد مساحة عملك تسمية إحدى مهاراتها الخاصة (nameAr / nameEn) أو تحذفها في التطبيق، يتقدّم updatedAt لكل نسخة مجموعة وظيفية ووصف وظيفي في مساحة عملك يسردها (فيلتقطه updatedSince)، ويُرسل job_family.updated (changedFields: ["instances"]) وjob_description.updated (changedFields: ["skills"])، أو <resource>.bulk_changed إذا تجاوز العدد 50. أما تعديل وصف المهارة أو وسمها أو أوصاف مستوياتها فقط فلا يُرسل شيئًا (فهي ليست في كائنات v1).

  • مهام الدفعات تتوقف عند تعليق مساحة العمل، والسيرة الذاتية للمرشح تُجلب مرة واحدة: أصبحت مهمة الدفعات الجارية تتوقف بعد الدفعة التي تعالجها عند تعليق مساحة عملها (سابقاً كانت تنتظر المهمة التي لم تبدأ فقط)، ثم تُستأنف من حيث توقفت عندما تعود مساحة العمل نشطة. ويجب أن يكتمل تنزيل cvUrl في صف المرشح خلال 20 ثانية إجمالاً (سابقاً كان للبحث عن العنوان وللتنزيل 20 ثانية لكل منهما)، وإلا يفشل الصف بـ422 validation_failed (cv_unreachable). ولم يعد الصف الذي يتكرر بعد تشغيل متقطع ينزّل سيرته مرة أخرى، فلم يعد يرسل candidate.updated ثانياً ولا يكتب صف تدقيق ثانياً.

  • سجل النشاط في لوحة المنصة يسمّي التكامل وراء التغيير عبر الواجهة البرمجية: في سجل النشاط بلوحة إدارة المنصة (عند التصفية على مساحة عمل)، تظهر التغييرات التي تمت بمفتاح API لمساحة العمل الآن بصيغة تكامل · <اسم التكامل> بدلاً من مستخدم مساحة عمل عام. والصفوف التي يكتبها تكامل على مستوى النشر (لا يوجد منها شيء اليوم، فمفاتيح مستوى النشر للقراءة فقط) تُربط به ولا تتبع أي مساحة عمل (فتظهر في السجل دون تصفية) بصيغة تكامل على مستوى النشر · <الاسم>، ويكون actor.id لها في audit.recorded معرّف التكامل على مستوى النشر. ولم يتغيّر شيء آخر في /api/v1 ولا في حمولات خطافات الويب.

2026-10-24

  • إصلاحات ما قبل الإطلاق (قبل إتاحة الواجهة البرمجية للعموم): أصبح validation_failed دائماً 422 (كان 400 لقيمة خاطئة في limit أو cursor أو updatedSince أو since، أو جسم ليس JSON، أو غياب Idempotency-Key). ويقبل POST /api/v1/openings الحقل posting: { ar, en } ويحفظه نصاً لإعلان الوظيفة، فيمكن فتح الوظائف المُنشأة عبر الواجهة البرمجية (ودونه: 422 validation_failed مع details.reason بقيمة no_job_description)؛ وفتح وظيفة وصفها الوظيفي مؤرشف يعيد 422 validation_failed (مع details.reason بقيمة archived). وإسناد موظف إلى وظيفة في الهيكل التنظيمي يرسل employee.updated (changedFields: ["currentPositionId"])؛ وضبط status لموظف على FORMER_EMPLOYEE يرسل أيضاً employee.offboarded؛ والمجموعات المضافة أثناء إعداد مساحة العمل ترسل job_family.created في المرة الأولى. والحفظ الذي لا يغيّر شيئاً (وحدة تنظيمية أو وظيفة) لم يعد يكتب سطر تدقيق ولا يرسل .updated. وحقول الأجر دون structure.functional.salary تُرفض بـ 403 قبل البحث عن سلم الرواتب. خطافات الويب: تُسقط التسليمات المنتظرة (scope changed) حين يفقد التكامل صلاحية أو تتوقف النقطة عن الاشتراك في نوع حدثها؛ وتُبنى إعادة الإرسال بالصلاحيات الحالية وتُرفض (409 / 403) خارج النطاق، وتتطلب أن تكون النقطة والتكامل نشطين؛ والتدوير الثاني للسر خلال مدة الـ 24 ساعة يعيد 409 conflict (مع details.reason بقيمة rotation_in_progress). سجل التدقيق وaudit.recorded: تُستبعد حقول التقييم الداخلية من changes في صفوف الأوصاف الوظيفية؛ ويبقى منفّذ التكامل type: "integration" (مع id: null) بعد حذف التكامل؛ وتستخدم صفوف المنصة target.type بصيغة snake_case وتشمل تعديلات دليل جديرون (taxonomy.*)؛ وأصبح إظهار سر التوقيع يُسجَّل (settings.integration.secret_revealed وadmin.integration.endpoint_secret_revealed). ومهام الإدراج المجمّع تتوقف بين الدفعات عند إيقاف التكامل، وتُفشل الصفوف المتبقية بـ forbidden عند إزالة صلاحية لازمة، وتعدّ السيرة الذاتية على خادم غير عام خطأ صف 422، وتحذف صفوفها المرسلة عند DONE.
  • التوثيق: صفحات أدلة جديدة (نظرة عامة، والأعراف مع القائمة الكاملة لرموز الأخطاء، والمزامنة مع ERP، والترحيل المجمّع، وخطافات الويب مع التحقق من التوقيع بلغتي Node وPython، وفهرس الأحداث المولّد، وسجل التدقيق لأنظمة SIEM، وصفحة لكل مرحلة من دورة الحياة) ومرجع تفاعلي للواجهة البرمجية معروض من GET /api/v1/openapi.json. لم يتغير أي سلوك في الواجهة البرمجية.
  • نقاط استقبال خطافات الويب لتكاملات مساحة العمل: يديرها مسؤول مساحة العمل من الإعدادات ← التكاملات (مع صلاحيات التكامل ومفاتيح API الخاصة به). يجب أن تكون أنواع أحداث نقطة الاستقبال * أو audit.recorded أو نوعاً من فهرس الأحداث (<resource>.<verb>، مثل employee.updated)؛ ولا يمكن اختيار audit.recorded إلا إذا كان التكامل يملك audit.view، ولا تشمله * أبداً. ويكتب إرسال حدث اختبار (ping) وإعادة إرسال حدث الآن إجراءَي التدقيق settings.integration.endpoint_tested وsettings.integration.delivery_redelivered، ويظهران في GET /api/v1/audit-events وفي audit.recorded.
  • تكامل القراءة على مستوى النشر: في النشر المخصص، يستطيع مشرف المنصة إنشاء تكامل للقراءة فقط من لوحة تحكم المنصة يقرأ جميع مساحات العمل النشطة. تعمل مفاتيحه على كل نقاط GET عدا GET /api/v1/bulk-jobs/{id}، وتعيد عمليات الكتابة 403. يحمل كل كائن يقرؤه workspaceId، وتقبل القوائم ?workspaceId= لحصر النتائج في مساحة عمل واحدة (404 لمساحة غير معروفة أو غير نشطة). وتتطلب نقاط الهيكل الوظيفي (/sectors و/sub-sectors و/job-families) المعامل ?workspaceId= لهذه المفاتيح وتعيد عرض تلك المساحة. لا تُخفى بيانات التواصل عنه. ويعيد GET /api/v1/audit-events صفوف كل مساحة عمل نشطة إضافة إلى صفوف المنصة (workspaceId: null). وتستقبل نقاط خطافات الويب الخاصة به الأحداث من كل مساحة عمل نشطة. تحتاج المفاتيح إلى تاريخ انتهاء وإلى قائمة عناوين IP مسموحة على التكامل (422 بخلاف ذلك). لا تتغير مفاتيح مساحة العمل، وتتجاهل ?workspaceId=. ويُدرج مستند OpenAPI المعامل workspaceId في كل GET ويوسم كل عملية بـx-deployment-level. انظر القراءة على مستوى الجهة.
  • الإدراج المجمّع: تستقبل POST /api/v1/employees/bulk و/api/v1/candidates/bulk و/api/v1/org-units/bulk و/api/v1/positions/bulk من 1 إلى 1,000 صف مفتاح كل منها { system, externalId } (مع Idempotency-Key إلزامي) وتجيب بـ202 { jobId, statusUrl }؛ ويعيد GET /api/v1/bulk-jobs/{id} الحالة والأعداد ونتيجة كل صف (created أو updated أو failed مع رمز الخطأ)، للتكامل الذي أرسل المهمة فقط. يتبع كل صف قواعد طلب PUT المفرد بالمعرّف الخارجي نفسها، ويكتب سطر تدقيق وحدث خطاف ويب خاصين به. ويمكن لصفوف المرشحين أن تحمل cvUrl برابط https (ملف PDF حتى 5 ميغابايت). مهمتان على الأكثر لكل تكامل في الوقت نفسه (429 too_many_jobs). انظر الإدراج المجمّع.
  • بث السيرة الذاتية وخطاب العرض يخدم ملفات خاصة: أصبحت السير الذاتية ومستندات الأشخاص وشهادات المؤهلات وخطابات العروض ومرفقات الطلبات تُخزَّن بشكل خاص دون رابط عام. ويواصل GET /api/v1/candidates/{id}/cv وGET /api/v1/offers/{id}/file بثها كما هي (الصلاحيات نفسها وcontent-disposition: attachment نفسه)، وتبقى الملفات المرفوعة قبل التغيير قابلة للبث إلى أن تُنقل. لا تتغير أي حقول في الاستجابات، فالواجهة البرمجية لم تُعِد موقع التخزين قط.
  • سجل المحذوفات وخطافات الويب يتبعان صلاحيات القراءة للمفتاح: أصبح GET /api/v1/deleted يسرد فقط الموارد التي يستطيع مفتاحك قراءتها، بالصلاحية نفسها التي يطلبها مسار GET لكل مورد (مثلاً: المفتاح الذي لا يملك إلا structure.org.view يرى سجلات حذف org_unit وposition فقط). وطلب ?resource= لمورد لا يستطيع المفتاح قراءته يعيد 403 forbidden. أما نقاط استقبال خطافات الويب فكانت تتلقى الأحداث (ومنها <resource>.deleted و<resource>.bulk_changed) فقط للموارد التي يستطيع تكاملها قراءتها، مع إخفاء بيانات تواصل المرشح دون recruitment.candidate.contact؛ وترى التكاملات على مستوى المنصة كل الموارد.
  • المقابلات والمستخدمون والأدوار وسجل التدقيق: GET/POST /api/v1/interviews وGET/PATCH /api/v1/interviews/{id} (القراءة: recruitment.view؛ الكتابة: recruitment.act؛ يقترح PATCH مواعيد جديدة ما دامت المقابلة OFFERED، أو يلغيها بالقيمة { "status": "CANCELLED" })، مع الأحداث interview.scheduled وinterview.updated وinterview.cancelled وinterview.completed (وكذلك للمقابلات التي تُجدول أو يُعاد اقتراحها أو يُختار موعدها أو تُلغى أو تُقيَّم في التطبيق)؛ وGET /api/v1/users (حسابات الموظفين، دون كلمات مرور أو رموز أو بيانات التحقق الثنائي) وGET /api/v1/roles (مع مفاتيح صلاحياتها)، وكلاهما للقراءة فقط بالصلاحية users.view؛ وGET /api/v1/audit-events (audit.view)، سجل التدقيق لأداة SIEM لديك: الصفوف كما خُزّنت، الأقدم أولاً، وupdatedSince على occurredAt، وتُخفى قيم تواصل المرشح في changes دون recruitment.candidate.contact. ويُخفى person في المقابلة بالطريقة نفسها. ويحمل المستخدمون والأدوار updatedAt (فيلتقط updatedSince تغييرات الأدوار)، ويرسلون user.updated وuser.deleted وrole.created وrole.updated وrole.deleted؛ ويُسرد المستخدمون المُزالون والأدوار المحذوفة في سجل المحذوفات. وإلغاء مقابلة ملغاة أصلاً لا يغيّر شيئاً، وإلغاء مقابلة مكتملة يعيد 409 conflict (مع details.reason بقيمة interview_closed).
  • الشواغر والطلبات والعروض: GET/POST /api/v1/openings وGET /api/v1/openings/{id} وPOST /api/v1/openings/{id}/close (القراءة: recruitment.view؛ الفتح والإغلاق: recruitment.hiring.open كما في التطبيق)؛ وGET/POST /api/v1/applications وGET /api/v1/applications/{id} وPOST /api/v1/applications/{id}/stage وPOST /api/v1/applications/{id}/reject (القراءة: recruitment.view؛ الكتابة: recruitment.act؛ والإضافة تتبع مسار توظيف الشاغر كما في صفحة الوظائف) وPOST /api/v1/applications/{id}/hire (recruitment.hire وrecruitment.candidate.contact)؛ وGET /api/v1/offers وGET /api/v1/offers/{id} وGET /api/v1/offers/{id}/file (recruitment.view، للقراءة فقط، ويُبث الخطاب كملف). الأحداث: opening.created وopening.opened وopening.closed وopening.archived وopening.updated وapplication.created وapplication.stage_changed وapplication.rejected وapplication.updated وapplication.hired؛ ويرسل التعيين أيضاً employee.created، وتنتقل externalRefs الخاصة بالمرشح إلى الموظف. كما أصبح فتح التوظيف وإغلاقه من التطبيق يرسلان position.updated، والوظائف التي تُنشأ من مسار الشواغر ترسل position.created.

2026-10-22

  • الموظفون والمرشحون: GET/POST /api/v1/employees وGET/PATCH/DELETE /api/v1/employees/{id} وPOST /api/v1/employees/{id}/archive وPUT /api/v1/employees/by-external/{system}/{externalId} (القراءة: people.view؛ الكتابة: people.act)، وGET/POST /api/v1/candidates وGET/PATCH /api/v1/candidates/{id} وGET /api/v1/candidates/{id}/cv (القراءة: recruitment.view؛ الكتابة: recruitment.act)، مع الأحداث employee.created وemployee.updated وemployee.offboarded وemployee.archived وemployee.deleted وemployee.bulk_changed وcandidate.created وcandidate.updated وcandidate.archived وcandidate.deleted. يُنهي DELETE على الموظف خدمته (FORMER_EMPLOYEE)، ولا تحذف الواجهة البرمجية أي شخص. وتُخفى بيانات تواصل المرشح (البريد والجوال ورقم الهوية) في الاستجابات وحمولات خطافات الويب للمفاتيح التي لا تملك recruitment.candidate.contact. وتُبث السيرة الذاتية كملف للتنزيل، ولا يُعاد موقع تخزينها أبداً.
  • الأوصاف الوظيفية: GET/POST /api/v1/job-descriptions وGET/PATCH /api/v1/job-descriptions/{id} وPOST /api/v1/job-descriptions/{id}/archive وPOST /api/v1/job-descriptions/{id}/unarchive (القراءة: structure.functional.view أو recruitment.view؛ الكتابة: structure.functional.act)، مع الأحداث job_description.created وjob_description.updated وjob_description.archived وjob_description.unarchived وjob_description.deleted وjob_description.bulk_changed. يقبل POST وPATCH الحقل externalRef لربط معرّفك؛ ويترك PATCH الحقول ومصفوفات المهارات غير المرسلة كما هي؛ والأوصاف المأخوذة من الدليل الوطني (SSCO) للقراءة فقط (409 ssco_managed)؛ ولا يوجد DELETE. كما أصبحت إزالة القطاعات الفرعية أو المجموعات الوظيفية من التطبيق ترسل job_description.deleted وposition.updated لما أُزيل أو أُغلق، وحفظ محرر البيانات يرسل <resource>.bulk_changed.
  • الهيكل الوظيفي وسلالم الرواتب (للقراءة فقط): GET /api/v1/sectors وGET /api/v1/sub-sectors وGET /api/v1/job-families وGET /api/v1/job-families/{jobFamilyId} (structure.functional.view) وGET /api/v1/pay-scales مع المراتب والدرجات (structure.functional.salary)، مع الأحداث sector.* وsub_sector.* وjob_family.* وpay_scale.*. صفوف الدليل تحمل slug ثابتاً، والمجموعات الوظيفية تسرد نسخ مساحة عملك (الصفة والقطاعات الفرعية والمهارات).
  • الوظائف: GET/POST /api/v1/positions وGET/PATCH/DELETE /api/v1/positions/{id} وPOST /api/v1/positions/{id}/archive وPUT /api/v1/positions/by-external/{system}/{externalId}، مع الأحداث position.created وposition.updated وposition.archived وposition.deleted وposition.bulk_changed. حقول التوظيف للقراءة فقط، وحقول الأجر تتطلب structure.functional.salary.
  • الوحدات التنظيمية: GET/POST /api/v1/org-units وGET/PATCH/DELETE /api/v1/org-units/{id} وPUT /api/v1/org-units/by-external/{system}/{externalId}، مع الأحداث org_unit.created وorg_unit.updated وorg_unit.deleted وorg_unit.bulk_changed. حذف وحدة يرسل position.updated لكل وظيفة تصبح بلا وحدة.
  • إطلاق الواجهة البرمجية v1: التكاملات بنطاقات صلاحيات، ومفاتيح الواجهة البرمجية (Authorization: Bearer jdr_live_…، تظهر مرة واحدة، مع تاريخ انتهاء اختياري)، وقائمة عناوين IP مسموحة اختيارية، و600 طلب في الدقيقة لكل مفتاح (429 rate_limited مع Retry-After)، وGET /api/v1/ping، وGET /api/v1/openapi.json العام، والترقيم بالمؤشر مع updatedSince في كل قائمة، وسجل المحذوفات GET /api/v1/deleted (تُحفظ آثار الحذف 180 يومًا)، وIdempotency-Key في POST (24 ساعة)، وقائمة رموز الأخطاء المغلقة، وخطافات الويب الموقّعة (Jadiroon-Signature، و10 محاولات بفواصل متزايدة على مدى نحو 3 أيام و20 ساعة حين يعمل مُرسِل إعادة المحاولة كل دقيقة، والتعطيل التلقائي بعد 3 أيام من الفشل، والحفظ 30 يومًا، والحدث الاختياري audit.recorded). تظهر عمليات الكتابة عبر الواجهة البرمجية في سجل التدقيق باسم تكامل · (الاسم). والسجلات الموجودة قبل الإطلاق تحمل updatedAt = تاريخ الإطلاق، فتعيد أول مزامنة بـ updatedSince كل شيء.