توثيق API الخاص بـ YA-Planning
ادمج توليد الجداول الزمنية والاطّلاع عليها، وأدِر إعداد مؤسساتك، وتلقَّ الأحداث في الوقت الفعلي. مرجع كامل، وأمثلة، وبيئة اختبار.
مقدّمة
واجهة API من نوع REST، بصيغة JSON، ومُصدَرة بإصدارات. تعيش جميع موارد الأعمال تحت البادئة /api/v1/pub/v1. نقاط النهاية التجريبية العامة تحت /api/v1/public/api-demo.
- عنوان URL الأساسي :
https://votre-domaine/api/v1/pub/v1 - الصيغة : الطلبات والاستجابات بـ
application/json(عدا تصديرات ICS/PDF). - الإصدارات : الإصدار موجود في المسار (
/v1)؛ الإضافات المتوافقة مع الإصدارات السابقة لا تغيّر الإصدار. - CORS : واجهة API العامة مفتوحة لتطبيقات المتصفّح الخارجية (دون ملفات تعريف ارتباط).
المصادقة
يحمل كل طلب مفتاح API، في أحد ترويسين اثنين:
X-API-Key: yap_live_xxxxxxxxxxxxxxxxxxxx
# ou
Authorization: Bearer yap_live_xxxxxxxxxxxxxxxxxxxxيمكن أن يُصرَّح لمفتاح بالوصول إلى عدّة مؤسسات (مفتاح شركات). حدِّد عندئذٍ الهدف عبر الترويسة X-Etablissement-Id؛ وإن كانت مؤسسة واحدة فقط مُصرَّحًا بها، فتُستنتَج تلقائيًا. تُنشأ المفاتيح، بنطاقات، من وحدة التحكم.
البيئات وبيئة الاختبار
بيئتان، تتميّزان ببادئة المفتاح:
yap_live_…— الإنتاج : بياناتك الحقيقية؛ ويخصم التوليد من الحصة الأسبوعية.yap_test_…— sandbox : تُعيد عمليات القراءة مجموعة بيانات تجريبية (وليست بياناتك الحقيقية أبدًا)، ولا يُخصَم التوليد، ويُرفض كتابة الإعداد (SANDBOX_READONLY). مثالي للتطوير دون مخاطرة.
النطاقات
يحمل كل مفتاح مجموعة فرعية من النطاقات؛ وترفض نقطة النهاية (403 INSUFFICIENT_SCOPE) إذا كان النطاق المطلوب مفقودًا.
config:read | قراءة الأطوار والأقسام والمواد والأساتذة والفترات الزمنية والأحجام الزمنية. |
config:write | إنشاء / تعديل / حذف إعداد المؤسسة. |
timetables:read | سرد المهام، وقراءة النتائج والجداول الزمنية المنشورة، والتصدير. |
timetables:write | بدء توليد ونشر جدول زمني. |
webhooks:manage | إنشاء وسرد وحذف نقاط نهاية webhook. |
establishments:manage | إنشاء وسرد مؤسساتك الخاصة (على مستوى الحساب، دون X-Etablissement-Id). |
المعدّل والحصص
لكل مفتاح حدّ للمعدّل (افتراضيًا 60 req/min). تحمل الاستجابات:
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 59
X-RateLimit-Reset: 1783919311 # epoch (s) de réinitialisationيُعيد التجاوز 429 RATE_LIMITED مع الترويسة Retry-After. ويخضع توليد الجداول الزمنية إضافةً إلى ذلك لـ الحصة الأسبوعية للخطة (تظهر عبر /me).
تحمل كل استجابة من الواجهة أيضًا ترويسة X-Request-Id (معرّف ارتباط) — أبلِغ به الدعم عند حدوث عطل. ملاحظة : يُطبَّق حساب المعدّل حاليًا لكل نسخة؛ وفي نشر متعدّد النسخ قد يكون الحدّ الفعلي مضاعفًا للقيمة المعروضة.
الأخطاء
غلاف خطأ موحّد، مع requestId للدعم:
{ "error": "INSUFFICIENT_SCOPE", "message": "Scope requis : config:write.",
"requestId": "6181762d-…", "requiredScope": "config:write" }| الرمز | error | السبب |
|---|---|---|
| 400 | BAD_REQUEST | معاملات مفقودة أو غير صالحة. |
| 401 | UNAUTHORIZED / INVALID_API_KEY | مفتاح مفقود أو غير صالح أو مُبطَل. |
| 403 | INSUFFICIENT_SCOPE | المفتاح لا يملك النطاق المطلوب. |
| 403 | ETABLISSEMENT_NOT_GRANTED | المفتاح غير مُصرَّح له بالمؤسسة المطلوبة. |
| 403 | SANDBOX_READONLY | الكتابة ممنوعة بمفتاح sandbox (قراءة فقط). |
| 402 | PLAN_UPGRADE_REQUIRED | وظيفة محصورة بخطة مدفوعة (مثل تصدير iCal). |
| 404 | NOT_FOUND | مورد غير موجود أو خارج مؤسستك. |
| 409 | WORKFLOW_ERROR | انتقال في سير العمل غير ممكن (مثل نشر مسودة). |
| 429 | RATE_LIMITED | تجاوز المعدّل — أعد المحاولة بعد ترويسة Retry-After. |
اختبر مباشرةً — دون تسجيل
نقاط نهاية تجريبية عامة (لا يلزم مفتاح).
Postman : « Import » → « Link » → الصق /q/openapi?format=json.
مرجع — الحساب والاستبطان
/meتفاصيل المفتاح الحالي: البيئة، المؤسسة الهدف، النطاقات الممنوحة، والحصة المتبقية من التوليد. لا يلزم أي نطاق.
{
"environment": "LIVE",
"etablissementId": "61a9…8cb",
"scopes": ["config:read", "timetables:write"],
"entitlements": {
"plan": "PRO",
"generationsRestantes": -1,
"resetLe": "2026-07-14T00:00:00Z",
"maxClassesExport": -1
}
}/me/etablissementالملف العام للمؤسسة الهدف: الاسم، الاختصار، المدينة، البلد، النوع، الموقع الإلكتروني. لا يلزم أي نطاق.
{"id":"…","nom":"Groupe Scolaire Les Palmiers","slug":"gs-palmiers","sigle":"GSP","ville":"Abidjan","pays":"CI","type":"PRIVE","siteWeb":"https://…"}/me/usageالاستخدام الحديث للمفتاح الحالي (الطلبات والأخطاء يوميًا). لا يلزم أي نطاق.
days— نافذة بالأيام (افتراضيًا 30، بحدّ أقصى 90)
{"apiKeyId":"…","days":30,"daily":[{"day":"2026-07-12","calls":128,"errors":3}]}مرجع — الحساب — المؤسسات
التجهيز على مستوى الحساب: أنشئ مؤسساتك الخاصة دون ترويسة X-Etablissement-Id (المسار /account غير مرتبط بمؤسسة). النطاق establishments:manage. عند الإنشاء، يحصل المفتاح المُنادِي تلقائيًا على منح ACTIVE على المؤسسة الجديدة — ثم استخدم X-Etablissement-Id لإعدادها والتوليد.
/account/etablissementsestablishments:manageإنشاء مؤسسة (حصّة المطوّر، خاضعة لسقف حسابك).
{"nom":"Groupe Scolaire Les Palmiers","type":"PRIVE_LAIC","pays":"CI"}{"id":"…","nom":"Groupe Scolaire Les Palmiers","slug":"gs-les-palmiers","pays":"CI","type":"PRIVE_LAIC"}/account/etablissementsestablishments:manageسرد المؤسسات التي أنشأتها.
[{"id":"…","nom":"…","slug":"…","pays":"CI","type":"PRIVE_LAIC"}]مرجع — الإعداد
البنية التربوية للمؤسسة. القراءة: النطاق config:read. الكتابة: النطاق config:write (ممنوعة في sandbox). تحتوي المؤسسة المُنشأة حديثًا بالفعل على « طور رئيسي ».
/cyclesconfig:readقائمة الأطوار.
[{"id":"…","libelle":"Cycle principal","ordre":0}]/cyclesconfig:writeإنشاء طور.
{"libelle":"Second cycle","ordre":1}/cycles/{id}config:writeحذف طور.
/niveauxconfig:readقائمة الأقسام/المستويات.
[{"id":"…","libelle":"6ème 1","cycleId":"…","effectif":42,"ordre":1}]/niveauxconfig:writeإنشاء قسم.
{"libelle":"6ème 3","cycleId":"<uuid-cycle>","effectif":40,"ordre":3}/niveaux/{id}config:writeتعديل قسم (حقول جزئية).
/niveaux/{id}config:writeحذف قسم.
/disciplinesconfig:readقائمة المواد.
[{"id":"…","libelle":"Mathématiques","code":"MATH","couleur":"#6C5CE7","ordre":1}]/disciplinesconfig:writeإنشاء مادة.
{"libelle":"Physique-Chimie","code":"PC","couleur":"#00B894","ordre":5}/disciplines/{id}config:writeتعديل مادة.
/disciplines/{id}config:writeحذف مادة.
/professeursconfig:readقائمة الأساتذة.
[{"id":"…","nom":"Koné","prenoms":"Awa","matricule":"P-0142","email":"…","telephone":"…","nombreHeureAutorise":18,"statut":"TITULAIRE","actif":true}]/professeursconfig:writeإنشاء أستاذ.
{"nom":"Diarra","prenoms":"Sékou","matricule":"P-0210","email":"s.diarra@ecole.ci","nombreHeureAutorise":18,"statut":"VACATAIRE","actif":true}/professeurs/{id}config:writeتعديل أستاذ.
/professeurs/{id}config:writeحذف أستاذ.
/horairesconfig:readالفترات الزمنية للشبكة.
[{"id":"…","jour":"MONDAY","heureDebut":"08:00","heureFin":"09:00","estPause":false,"libelle":null}]/horairesconfig:writeإنشاء فترة زمنية (يوم، HH:mm).
{"jour":"MONDAY","heureDebut":"08:00","heureFin":"09:00","estPause":false,"libelle":"M1"}/horaires/{id}config:writeحذف فترة زمنية.
/volumesconfig:readالأحجام الزمنية (المادة × الطور).
[{"id":"…","cycleId":"…","disciplineId":"…","nombreHeuresObligatoire":4,"minHeuresConsecutives":1,"maxHeuresConsecutives":2}]/volumesconfig:writeإنشاء حجم زمني.
{"cycleId":"<uuid>","disciplineId":"<uuid>","nombreHeuresObligatoire":4,"minHeuresConsecutives":1,"maxHeuresConsecutives":2}/volumes/{id}config:writeحذف حجم زمني.
مرجع — الجداول الزمنية
توليد غير متزامن (يعمل المحرّك في الخلفية — استعلم عن الحالة)، ونشر واطّلاع. في sandbox، يستخدم التوليد مجموعة بيانات تجريبية ولا يُخصَم من الحصة.
/timetables/generatetimetables:writeيبدأ توليدًا. يُعيد 202 + jobId للاستعلام عنه. تخصم LIVE من الحصة الأسبوعية. ترويسة اختيارية Idempotency-Key : يُعيد المعرّف نفسه تشغيل الاستجابة المحفوظة (استجابة موسومة بـ Idempotency-Replayed: true) بدلًا من بدء توليد جديد.
Idempotency-Key— ترويسة (اختيارية) — مفتاح idempotence على جانب LIVE
{"durationMinutes": 5}{"jobId":"…","status":"SOLVING_SCHEDULED"}/timetables/jobstimetables:readقائمة مقسّمة إلى صفحات للمهام.
page— فهرس الصفحة (افتراضيًا 0)size— الحجم (افتراضيًا 25، بحدّ أقصى 100)
{"items":[{"jobId":"…","name":"…","status":"SOLVING_ENDED","workflowStatus":"BROUILLON","feasible":true,"hardScore":0,"softScore":-42,"createdAt":"…"}],"total":7,"page":0,"size":25}/timetables/jobs/{jobId}timetables:readحالة مهمة.
/timetables/jobs/{jobId}/resulttimetables:readالنتيجة: قائمة الحصص (بمجرّد status = SOLVING_ENDED).
{"jobId":"…","status":"SOLVING_ENDED","feasible":true,"lessons":[{"id":"1","matiere":"Maths","niveau":"6ème 1","professeur":"M. Koné","jour":"MONDAY","debut":"08:00:00","fin":"09:00:00","couleur":"#6C5CE7","niveauOrdre":1,"verrouille":false}]}/timetables/jobs/{jobId}/publishtimetables:writeينشر الجدول الزمني (سير العمل → PUBLIE). يُطلق حدث webhook المسمّى timetable.published.
/timetables/published/latesttimetables:readآخر جدول زمني منشور للمؤسسة.
/timetables/published/{jobId}/classestimetables:readتسميات الأقسام الموجودة في جدول زمني منشور.
{"classes":["6ème 1","6ème 2","5ème 1"]}/timetables/published/{jobId}/classe/{niveau}timetables:readحصص قسم في جدول زمني منشور.
/timetables/jobs/{jobId}/export.icstimetables:readتصدير iCalendar (text/calendar). محصور بخطة Pro → 402 خلاف ذلك.
niveau— التصفية حسب قسمprofesseur— التصفية حسب أستاذ
/timetables/jobs/{jobId}/export.pdftimetables:readتصدير PDF (application/pdf) — الدفعة كاملة، حسب القسم أو حسب الأستاذ.
mode— all | class | teacherniveau— مطلوب إذا mode=classprofesseur— مطلوب إذا mode=teacher
مرجع — Webhooks
تلقَّ أحداثًا موقّعة. النطاق webhooks:manage. لا يُعاد السر إلا عند الإنشاء (أو بعد التدوير). الأحداث : timetable.job.completed، timetable.job.failed، timetable.published، webhook.test. كل عملية تسليم موقّعة (HMAC-SHA256) وتُعاد حتى 5 مرات عند الفشل. يُتحقَّق من عنوان URL ضدّ SSRF (تُرفض الأهداف الداخلية/الخاصة).
/webhookswebhooks:manageقائمة نقاط النهاية.
/webhookswebhooks:manageإنشاء نقطة نهاية. يُعيد سر التوقيع (احتفظ به).
{"url":"https://exemple.com/webhooks/yap","events":["timetable.published","timetable.job.completed"]}{"endpoint":{"id":"…","url":"…","events":["timetable.published"],"active":true},"secret":"whsec_…"}/webhooks/{id}webhooks:manageحذف نقطة نهاية.
/webhooks/{id}/deliverieswebhooks:manageسجل عمليات التسليم (الحالة، رمز HTTP، المحاولات).
/webhooks/{id}/rotatewebhooks:manageإعادة توليد سر التوقيع (يُعاد مرة واحدة فقط).
{"endpoint":{"id":"…","url":"…"},"secret":"whsec_…"}/webhooks/{id}/testwebhooks:manageإرسال حدث webhook.test إلى نقطة النهاية (202 + status queued).
/webhooks/deliveries/{deliveryId}/replaywebhooks:manageإعادة تشغيل عملية تسليم سابقة (202 + status queued).
مرجع — عرض (عام، دون مفتاح)
نقاط نهاية عامة غير مصادَق عليها لاكتشاف شكل الاستجابات.
/api/v1/public/api-demo/previewمعاينة مؤسسة تجريبية (أطوار، أقسام، مواد، أساتذة، فترات زمنية).
/api/v1/public/api-demo/sample-timetableمثال على الحصص (شكل جدول زمني مُولَّد).
Webhooks — التحقّق من توقيع
كل عملية تسليم هي POST بصيغة JSON مع الترويسات X-Webhook-Event وX-Webhook-Timestamp وX-Webhook-Signature = HMAC-SHA256(timestamp + "." + body, secret). تحقّق من التوقيع ومن حداثة الطابع الزمني (أقل من 5 دقائق) لتفادي إعادة التشغيل. الأحداث : timetable.published، timetable.job.completed، timetable.job.failed، webhook.test.
POST https://votre-app/webhook
X-Webhook-Event: timetable.published
X-Webhook-Timestamp: 1712345678
X-Webhook-Signature: 9f86d081…
{ "event":"timetable.published", "jobId":"…", "etablissementId":"…", "occurredAt":"…" }import crypto from "crypto";
// Express — vérifier la signature d'un webhook YA-Planning
app.post("/webhooks/yap", express.raw({ type: "*/*" }), (req, res) => {
const ts = req.header("X-Webhook-Timestamp");
const sig = req.header("X-Webhook-Signature");
const body = req.body.toString("utf8");
const expected = crypto.createHmac("sha256", process.env.YAP_WEBHOOK_SECRET)
.update(`${ts}.${body}`).digest("hex");
// fraîcheur (anti-rejeu) : < 5 min
const fresh = Math.abs(Date.now()/1000 - Number(ts)) < 300;
if (!fresh || !crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected)))
return res.sendStatus(400);
const event = JSON.parse(body); // { event, jobId, etablissementId, occurredAt }
res.sendStatus(200);
});أمثلة برمجية
# Vérifier ses droits
curl https://votre-domaine/api/v1/pub/v1/me \
-H "X-API-Key: yap_live_xxxxxxxxxxxx"
# Générer un EDT puis suivre le job
JOB=$(curl -s -X POST .../api/v1/pub/v1/timetables/generate \
-H "X-API-Key: yap_live_xxxx" -H "Content-Type: application/json" \
-d '{"durationMinutes":5}' | jq -r .jobId)
curl .../api/v1/pub/v1/timetables/jobs/$JOB \
-H "X-API-Key: yap_live_xxxx"الإصدارات والإهمال
واجهة API مُصدَرة في المسار (/api/v1/pub/v1/…). نضيف حقولًا ونقاط نهاية بطريقة متوافقة مع الإصدارات السابقة؛ ولا تشكّل هذه الإضافات كسرًا، لذا برمِج بتسامح (تجاهل الحقول غير المعروفة). أي كسر سيؤدّي إلى إصدار رئيسي جديد وإلى إشعار مسبق لا يقل عن 90 يومًا، يُعلَن عبر ترويسة الاستجابة Deprecation وسجل التغييرات أدناه.
2026-07— حسابات المطوّرين ولوحة التحكم؛ تجهيز على مستوى الحساب (POST /account/etablissements، النطاقestablishments:manage)؛ نقاط النهاية/cycles؛ تصدير PDFmode=teacher.2026-07— Idempotency-Key على التوليد؛ نقاط النهاية/me/etablissementو/me/usage؛ تدوير / اختبار / إعادة تشغيل webhooks؛ الحدثtimetable.job.failed؛ الترويسةX-Request-Id؛ ترقيم صفحات قوائم الإعداد؛ انتهاء صلاحية المفاتيح وتقييدها بعنوان IP؛ تحقّق مضاد لـ SSRF من عناوين URL الخاصة بـ webhook.2026-06— إطلاق واجهة API العامة v1 : مفاتيح بنطاقات، إعداد، توليد ونشر الجداول الزمنية، webhooks موقّعة، بيئة اختبار، ترويسات الحصص.
