مرجع واجهة API
كل مسار تقدّمه واجهة API، ومن يستطيع استدعاءه، وكيف يعمل تسجيل الدخول ومساحات العمل والصلاحيات والفوترة ومفاتيح API والأخطاء والقوائم والحدود.
لحزمة الحزمة الكاملة
العنوان الأساسي وصيغة الاستجابة
كل مسار يُقدَّم تحت البادئة /api. محليًا يكون العنوان الأساسي http://localhost:8000/api. وعلى الخادم يكون عنوان واجهة API متبوعًا بـ /api، وهو القيمة نفسها التي يقرؤها الموقع ولوحة التحكم من NEXT_PUBLIC_API_BASE_URL (متغيرات البيئة). الملفات تُقدَّم خارج البادئة: ملفات هوية الحزمة على /media/… والملفات المرفوعة على /media/uploads/….
أجسام الطلبات بصيغة JSON، باستثناء رفع الملفات. وكل إجابة تأتي في المغلّف نفسه:
{
"success": true,
"data": { },
"message": ""
}يحمل data النتيجة. أما message فهو رمز قصير تملؤه بعض عمليات الكتابة، مثل project_created أو invitation_sent، ويكون فارغًا في غير ذلك. تُسمّى مساحات العمل والمشاريع والمهام بمعرّفاتها العامة (org_forma وprj_01)؛ والأعضاء والدعوات والمفاتيح والملفات برقمها. وتحمل نصوص المشاريع والمهام اللغتين معًا بالشكل { "en": { … }, "ar": { … } }.
فحص السلامة لا يحتاج إلى رمز:
curl http://localhost:8000/api/health{"success":true,"data":{"status":"ok","database":"up"},"message":""}إلى أن تجيب قاعدة البيانات وتصبح جداولها جاهزة، تجيب الواجهة بـ 503 مع "status":"unavailable" و"database":"down". يقرأ فحص الصحة في صورة Docker هذه الحالة، ولا يشغّل Docker Compose الموقع ولوحة التحكم إلا بعد أن ينجح.
لا يجوز للمتصفحات استدعاء واجهة API إلا من العناوين الواردة في CORS_ORIGIN. وإذا تُرك غير مضبوط فهي تسمح بـ http://localhost:3030 وhttp://localhost:3031، وفي الإنتاج ترفض الواجهة البدء من دونه (مشكلات CORS).
الأخطاء
يحتفظ الفشل بالمغلّف نفسه، مع success: false وdata: null ورمز حالة HTTP:
{
"success": false,
"data": null,
"message": "seat_limit_reached",
"errors": { "email": ["…"] }
}- يظهر
errorsعند فشل التحقق (400)، مع المشكلات لكل حقل. - أي حقل غير معروف في الجسم يُرفض بالرمز
400: كل جسم يقبل الحقول المذكورة أدناه فقط. - عندما يكون لدى واجهة API جملة للخطأ، يكون
messageتلك الجملة بلغة الطلب (Accept-Language: arللعربية): تسجيل دخول مرفوض، وكل429، والتسجيل المغلق، وحساب محمي من تغيير كلمة المرور أو البريد، وسر webhook مفقود، واشتراك تعذّر على Stripe إلغاؤه، ودور منصة أو صلاحية لا يملكها المستدعي، ووضع الصيانة. - وفي غير ذلك، يكون
messageفي401و403و404جملة عامة للحالة، وفي400و402و409و410و503يكون السبب رمزًا قصيرًا (seat_limit_reachedوslug_takenوover_plan_limitوpayment_declinedوpreview_expiredوinvitation_expiredوpayment_unavailable)، يحوّله العميل إلى جملته الخاصة. والتخفيض المرفوض بسبب الاستخدام يحملblockersأيضًا. - يعني
402ترقية خطة لم يقبل مزود الدفع تحصيلها:payment_declinedأوpayment_needs_action(يطلب المصرف تأكيد الدفعة) أوpayment_method_missing. وتبقى الخطة كما كانت. - أثناء تفعيل طاقم المنصة لوضع الصيانة، يجيب كل مسار خارج القائمة القصيرة في وضع الصيانة بـ
503لأي شخص ليس من طاقم المنصة، مع الجملة فيmessageوcode: "maintenance_mode". - يعني
401عدم وجود رمز، أو رمزًا منتهيًا، أو بيانات دخول خاطئة. ويعني403تسجيل دخول بلا الصلاحية، أو كتابة في مساحة عمل موقوفة. ويجيب404أيضًا عن مساحة عمل ليس المستدعي عضوًا فيها، فلا يعرف الغريب أي مساحات عمل موجودة. - الطلب الذي يتجاوز حدًّا يُجاب بـ
429(الحدود لكل عنوان).
تسجيل الدخول
تسجيل الدخول
الطرفيةcurl -X POST http://localhost:8000/api/auth/login \ -H "Content-Type: application/json" \ -d '{"email":"owner@northstack.example","password":"YOUR_PASSWORD"}'ضع بيانات الدخول الأولية من التثبيت مكان
YOUR_PASSWORD، أو استخدم حسابًا خاصًا بك.data.access_tokenهو الرمز، ومعهtoken_typeبقيمةBearerوexpires_inوadmin، وهو الحساب مع أدوار المنصة وصلاحياته ومساحات عمله.استدعِ مسارًا محميًا
الطرفيةcurl http://localhost:8000/api/workspaces \ -H "Authorization: Bearer YOUR_TOKEN"يسرد هذا مساحات عمل الحساب مع دوره في كل منها. ثم يأخذ كل مسار لمساحة عمل معرّفها العام في مساره:
الطرفيةcurl "http://localhost:8000/api/workspaces/org_forma/projects?limit=20" \ -H "Authorization: Bearer YOUR_TOKEN"
remember: trueيوقّع رمزًا لمدة 30 يومًا، وremember: falseرمزًا لمدة 12 ساعة؛ وإذا حُذف فالرمز يدوم بحسبJWT_EXPIRATION، وهو7dافتراضيًا.- بريد غير معروف وكلمة مرور خاطئة يُجابان بـ
401بالرسالة نفسها، بعد العمل نفسه من تجزئة كلمة المرور، فلا تدل الإجابة ولا زمنها على البريد الذي له حساب. - عشر محاولات دخول فاشلة من عنوان واحد خلال 15 دقيقة تُجاب بـ
429حتى يمضي 15 دقيقة على أقدم محاولة فاشلة (RATE_LIMIT_LOGIN). ولا تُحتسب عمليات الدخول الناجحة. - الرموز بلا حالة: يجيب
POST /api/auth/logoutفورًا، وتسجيل الخروج هو أن ينسى العميل رمزه.
| الطريقة | المسار | من يحق له استدعاؤه | وظيفته |
|---|---|---|---|
POST | /api/auth/login | أي شخص | تسجيل الدخول: email وpassword، وremember اختياريًا |
POST | /api/auth/signup | أي شخص | حساب جديد، يسجَّل دخوله فورًا وجاهز للتهيئة: name (من 2 إلى 80 حرفًا) وemail وpassword (من 8 إلى 128) وacceptTerms (true). البريد المستخدم يُجاب بـ 409 مع email_taken؛ وعندما يكون إعداد المنصة signups_enabled متوقفًا يُجاب بـ 403 |
POST | /api/auth/forgot-password | أي شخص | email، وlocale اختياريًا (en وar). يرسل بالبريد رابط إعادة تعيين يعمل مرة واحدة لمدة ساعة إذا كان الحساب موجودًا، ويجيب بالطريقة نفسها في الحالتين |
POST | /api/auth/reset-password | أي شخص | token (من الرابط) وpassword الجديدة (من 8 إلى 128). الرابط المستخدم أو المنتهي يُجاب بـ 401 |
POST | /api/auth/logout | أي شخص | يجيب بـ { signedOut: true } |
GET | /api/auth/me | أي حساب مسجّل الدخول | الحساب وأدوار المنصة وصلاحياته ومساحات عمله |
PATCH | /api/auth/me | أي حساب مسجّل الدخول | تغيير firstName أو lastName أو email أو phone |
PATCH | /api/auth/me/preferences | أي حساب مسجّل الدخول | theme (system وlight وdark) وreducedMotion وlocale (en وar) وlastWorkspaceId، وهي مساحة العمل التي يفتحها تسجيل الدخول التالي |
PATCH | /api/admins/profile/password | أي حساب مسجّل الدخول | تغيير كلمة مرور الحساب نفسه: current_password وpassword وpassword_confirmation |
مساحات العمل والصلاحيات
- مسارات مساحة العمل تبدأ بـ
/api/workspaces/:workspaceId، وهو المعرّف العام لمساحة العمل. في كل طلب تتحقق واجهة API من أن المستدعي له عضوية نشطة هناك (وإلا404) ويملك الصلاحية التي يذكرها المسار (وإلا403). الدور هو دور العضوية: يمكن لشخص واحد أن يكون مالك مساحة عمل ومسؤولًا في أخرى. - تضم مساحة العمل أربعة أدوار (
ownerوadminوmemberوbilling) ومصفوفتها الخاصة من 14 صلاحية:workspace.readوworkspace.updateوworkspace.deleteوprojects.readوprojects.writeوmembers.inviteوmembers.removeوroles.manageوbilling.readوbilling.manageوkeys.manageوaudit.readوai.readوai.apply. يملكها المالك كلها، وتبقىworkspace.deleteوroles.manageللمالك مهما قالت المصفوفة. وأي تغيير في المصفوفة يسري على الطلب التالي. - مساحة العمل الموقوفة تحتفظ بكل طلبات
GETوترفض كل طريقة أخرى بالرمز403. - مساحة العمل المتأخرة في الدفع تحتفظ بكل شيء حتى تمر
BILLING_GRACE_DAYS(7 افتراضيًا)؛ بعدها لا تستطيع إنشاء مشاريع أو دعوات أو مفاتيح API (403)، بينما تستمر كل القراءات وكل عمليات الكتابة الأخرى. - مسارات المنصة (
/api/platformو/api/usersو/api/adminsو/api/rolesو/api/settings) تتحقق بدلًا من ذلك من صلاحيات المنصة للحساب، وتُمنح عبر أدوار المنصة. ينشئ البذر مسؤول المنصة، بكل الصلاحيات الـ 21، والدعم، الذي يقرأ كل صفحات المنصة ويصدّر التقارير. والمسار الذي يذكر عدة صلاحيات يقبل أيًّا منها.
القوائم والتقسيم إلى صفحات
{ "data": [ ], "total": 12, "page": 1, "limit": 7, "totalPages": 2 }- المشاريع، والمشاريع المؤرشفة، والنشاط، ومحادثات المساعد ورسائلها، وصفحة مؤسسات المنصة ومستخدميها، تستخدم
pageوlimit، بحد أقصى 100 في الصفحة. القيم الافتراضية 7 مشاريع و12 حدثًا و20 محادثة و10 مؤسسات أو مستخدمين. - صفحات الطاقم وإعدادات المنصة وأدوارها تستخدم
pageوpage_countبدلًا من ذلك (10 افتراضيًا، وبحد أقصى 100). وأدوار المنصة لا تُقسَّم إلى صفحات إلا عند إعطاء الاثنين معًا؛ وإلا تُعاد كل الأدوار. - تأتي القوائم من الأحدث أولًا (المشاريع والمستخدمون والمؤسسات بحسب آخر تغيير)، مع المعرّف عند التعادل، ما لم يُعطَ
sort؛ وتأتي أدوار المنصة بحسب الاسم. وتعيد قائمة المشاريع أيضًاcountsلكل تبويب. - ما يخص مساحة عمل واحدة ويُعرض كاملًا يُجاب دون تقسيم إلى صفحات: مهام اللوحة، والأعضاء، والدعوات، ومفاتيح API، وملفات المشروع، وآخر 36 فاتورة، وآخر 8 إشعارات.
مساحات العمل والأعضاء ومصفوفة الأدوار
| الطريقة | المسار | من يحق له استدعاؤه | وظيفته |
|---|---|---|---|
GET | /api/workspaces | أي حساب مسجّل الدخول | مساحات عمل الحساب مع دوره في كل منها، لمبدّل مساحات العمل |
GET | /api/workspaces/slug-availability | أي حساب مسجّل الدخول | ?slug=: هل عنوان مساحة العمل متاح، مع suggestion عندما لا يكون كذلك |
POST | /api/workspaces | أي حساب مسجّل الدخول | التهيئة: name (من 2 إلى 80) وslug (من 3 إلى 48 من الأحرف الصغيرة والأرقام والواصلات) وregion (europe وnorth_america وasia_pacific)، وinvitations اختياريًا، حتى 20 عنصرًا { email, role }. يصبح المستدعي المالك، على فترة تجريبية للخطة Starter بمدة trial_days للمنصة. الـ slug المستخدم يُجاب بـ 409 مع slug_taken، وعندما يكون signups_enabled متوقفًا يُجاب بـ 403 |
GET | /api/workspaces/:workspaceId | عضو لديه workspace.read | مساحة العمل: الاسم والعنوان والخطة والاستخدام مقابل حدود الخطة وإعداداتها. تكون billingEmail بقيمة null ما لم يملك المستدعي billing.read أو billing.manage |
PATCH | /api/workspaces/:workspaceId | عضو لديه workspace.update | تغيير name أو slug أو locale (en وar) أو timeZone (منطقة IANA مثل Europe/Berlin) أو domainConfigured أو emailNotifications (رسائل الفوترة) أو weeklyDigest |
DELETE | /api/workspaces/:workspaceId | مالك مساحة العمل | confirmName (اسم مساحة العمل مكتوبًا) وpassword الخاصة بالمالك. تُحذف مساحة العمل حذفًا ناعمًا، ويُلغى اشتراكها في Stripe أولًا؛ وإذا رفض Stripe يُجاب بـ 409 وتبقى مساحة العمل |
GET | /api/workspaces/:workspaceId/members | عضو لديه workspace.read | الأعضاء مع أدوارهم. search وrole |
PATCH | /api/workspaces/:workspaceId/members/:memberId | عضو لديه roles.manage | تغيير role للعضو (admin وmember وbilling). صف المالك يُجاب بـ 403 |
DELETE | /api/workspaces/:workspaceId/members/:memberId | عضو لديه members.remove | إزالة عضو. لا يمكن إزالة المالك، ولا يجوز للمسؤول إزالة مسؤول آخر |
POST | /api/workspaces/:workspaceId/members/transfer-ownership | مالك مساحة العمل | memberId وpassword الخاصة بالمالك. يصبح العضو هو المالك ويصبح المالك السابق مسؤولًا |
GET | /api/workspaces/:workspaceId/roles | عضو لديه workspace.read | مصفوفة الأدوار: كل صلاحية، في ست مجموعات، والأدوار التي تملكها |
PUT | /api/workspaces/:workspaceId/roles | مالك مساحة العمل | حفظ المصفوفة: matrix، وفيها كل صلاحية مرتبطة بالأدوار التي تملكها. الصلاحية أو الدور غير المعروف يُجاب بـ 400 |
الدعوات
| الطريقة | المسار | من يحق له استدعاؤه | وظيفته |
|---|---|---|---|
GET | /api/workspaces/:workspaceId/invitations | عضو لديه workspace.read | status (pending، وهو الافتراضي، أو all). يجيب بـ invitations وseats، أي المقاعد المستخدمة والمتبقية في الخطة |
POST | /api/workspaces/:workspaceId/invitations | عضو لديه members.invite | دعوة email بدور role (admin وmember وbilling). يُرسل الرابط بالبريد ويعمل 7 أيام. الخطة الممتلئة تُجاب بـ 409 مع seat_limit_reached؛ ويُرفض already_member وalready_invited بالطريقة نفسها |
POST | /api/workspaces/:workspaceId/invitations/:id/resend | عضو لديه members.invite | رابط جديد صالح 7 أيام أخرى. مرة واحدة في الدقيقة على الأكثر (resend_too_soon)، وخمس مرات لكل دعوة (resend_limit_reached) |
DELETE | /api/workspaces/:workspaceId/invitations/:id | عضو لديه members.invite | إلغاء دعوة |
GET | /api/invitations/:token | أي شخص | ما يفتحه رابط الدعوة: اسم مساحة العمل وعنوانها، والدور، والبريد المدعو مخفيًا جزئيًا، وهل ما زالت الدعوة مفتوحة |
POST | /api/invitations/:token/accept | الحساب المدعو، وقد سُجّل دخوله | الانضمام إلى مساحة العمل. الرابط المنتهي أو الملغى يُجاب بـ 410؛ والحساب الذي بريده غير البريد المدعو يُجاب بـ 403؛ والخطة الممتلئة تُجاب بـ 409 مع seat_limit_reached |
تمر رسائل الدعوة عبر إعدادات SMTP الخاصة بواجهة API، أو تذهب إلى سجلها ما دام SMTP_HOST فارغًا، مع الرابط.
المشاريع والملفات
| الطريقة | المسار | من يحق له استدعاؤه | وظيفته |
|---|---|---|---|
GET | /api/workspaces/:workspaceId/projects | عضو لديه projects.read | المشاريع مع counts لكل تبويب. status (all أو active أو completed أو حالة واحدة) وstate (in_progress وreview وblocked وcompleted) وsearch وownerId وdueBefore (YYYY-MM-DD) وsort (updated_at وdue_date وname وprogress) وorder (asc وdesc) وpage وlimit |
GET | /api/workspaces/:workspaceId/projects/archived | عضو لديه projects.read | المشاريع المؤرشفة، بالمرشّحات نفسها |
GET | /api/workspaces/:workspaceId/projects/owners | عضو لديه projects.read | الأعضاء الذين يملكون مشاريع، لمرشّح المالك |
GET | /api/workspaces/:workspaceId/projects/:projectId | عضو لديه projects.read | مشروع واحد مع مالكه وتاريخ استحقاقه وتقدمه وعدد مهامه |
POST | /api/workspaces/:workspaceId/projects | عضو لديه projects.write | إنشاء مشروع: translations (en مطلوب وar اختياري، في كل منهما name حتى 120 حرفًا وdescription حتى 2000)، واختياريًا status وownerId (عضو) وdueDate (YYYY-MM-DD) وprogress (من 0 إلى 100) |
PATCH | /api/workspaces/:workspaceId/projects/:projectId | عضو لديه projects.write | تغيير أي من هذه الحقول |
DELETE | /api/workspaces/:workspaceId/projects/:projectId | عضو لديه projects.write | أرشفة مشروع. لا يُحذف شيء |
POST | /api/workspaces/:workspaceId/projects/:projectId/restore | عضو لديه projects.write | إعادة مشروع مؤرشف |
GET | /api/workspaces/:workspaceId/projects/:projectId/activity | عضو لديه projects.read | آخر 100 حدث في المشروع |
GET | /api/workspaces/:workspaceId/projects/:projectId/files | عضو لديه projects.read | ملفات المشروع، من الأحدث |
POST | /api/workspaces/:workspaceId/projects/:projectId/files | عضو لديه projects.write | إرفاق ملف: file بصيغة multipart، 10 ميغابايت على الأكثر. يُحتسب حجمه من تخزين الخطة |
GET | /api/workspaces/:workspaceId/projects/:projectId/files/:fileId/download | عضو لديه projects.read | url الملف وname وmime |
إنشاء مشروع في مساحة عمل متأخرة في الدفع انتهت مهلتها يُجاب بـ 403.
المهام واللوحة
تُعرض المهمة وتُنشأ تحت مشروعها، وتُغيَّر بمعرّفها الخاص تحت مساحة العمل.
| الطريقة | المسار | من يحق له استدعاؤه | وظيفته |
|---|---|---|---|
GET | /api/workspaces/:workspaceId/projects/:projectId/tasks | عضو لديه projects.read | مهام اللوحة بترتيب الأعمدة. assigneeId وpriority وsearch |
POST | /api/workspaces/:workspaceId/projects/:projectId/tasks | عضو لديه projects.write | إنشاء مهمة: translations (en مطلوب، في كل منهما title حتى 160 حرفًا وdescription حتى 4000)، واختياريًا status (backlog، وهو الافتراضي، وin_progress وreview وdone) وpriority (low وnormal وhigh) وassigneeId (عضو) وdueDate |
PATCH | /api/workspaces/:workspaceId/tasks/:taskId | عضو لديه projects.write | تغيير أي من هذه الحقول |
POST | /api/workspaces/:workspaceId/tasks/:taskId/move | عضو لديه projects.write | نقل بطاقة: status (العمود) وbeforeId، وهي البطاقة التي تستقر فوقها، أو لا شيء لنهاية العمود |
DELETE | /api/workspaces/:workspaceId/tasks/:taskId | عضو لديه projects.write | أرشفة مهمة |
المسؤول عن المهمة إذا لم يكن عضوًا يُجاب بـ 400 مع assignee_not_member. كل تغيير يكتب حدثًا في سجل النشاط، مثل task.moved مع العمودين اللذين انتقلت منهما وإليهما.
النظرة العامة والنشاط والإشعارات والبحث
| الطريقة | المسار | من يحق له استدعاؤه | وظيفته |
|---|---|---|---|
GET | /api/workspaces/:workspaceId/overview | عضو لديه workspace.read | النظرة العامة: المشاريع بحسب الحالة، والأعضاء والدعوات المعلقة، وأحداث المهام في كل يوم خلال آخر سبعة أيام، وقائمة الإطلاق، وحتى ثلاثة مشاريع يحل موعدها خلال الفترة، وآخر ستة أحداث. period (7d و30d، وهو الافتراضي، و90d). بدون projects.read تعود أعداد المشاريع والرسم البياني والمشاريع المستحقة أصفارًا أو فارغة |
GET | /api/workspaces/:workspaceId/activity | عضو لديه audit.read | سجل النشاط، مع الأشخاص فيه في actors. action (إجراء واحد مثل task.moved، أو عائلة تنتهي بنقطة مثل task.) وactorId وfrom وto (YYYY-MM-DD) وsearch وpage وlimit |
GET | /api/workspaces/:workspaceId/activity/export | عضو لديه audit.read | المرشّحات نفسها بصيغة ملف CSV (date وactor وaction وsubject وdetails)، حتى 5,000 صف. تُهرَّب الخلايا التي قد تعمل كمعادلة جدول بيانات |
GET | /api/workspaces/:workspaceId/notifications | عضو لديه workspace.read | الجرس: آخر 8 أحداث وعدد الأحداث الأحدث من آخر اطلاع للقارئ |
POST | /api/workspaces/:workspaceId/notifications/read | عضو لديه workspace.read | تعليم كل شيء حتى الآن كمقروء |
GET | /api/workspaces/:workspaceId/search | عضو لديه workspace.read | q (من 2 إلى 100 حرف): حتى خمسة مشاريع ومهام وأعضاء من كل نوع. المشاريع والمهام لا تظهر إلا لعضو لديه projects.read |
تعرض آخر أحداث النظرة العامة والجرس السجل كاملًا لمن لديه projects.read أو audit.read، وأحداث billing. فقط لمن لديه billing.read ولا يملك أيًّا من الصلاحيتين السابقتين، ولا شيء لأي شخص آخر.
الفوترة وwebhook الخاص بها والدفع التجريبي
| الطريقة | المسار | من يحق له استدعاؤه | وظيفته |
|---|---|---|---|
GET | /api/workspaces/:workspaceId/billing | عضو لديه billing.read | الاشتراك: الخطة والحالة والسعر والتجديد، وإلغاء أو تخفيض ينتظر التجديد، ومهلة التأخر في الدفع، والاستخدام، وأي دفع قيد التنفيذ |
GET | /api/workspaces/:workspaceId/billing/invoices | عضو لديه billing.read | آخر 36 فاتورة |
GET | /api/workspaces/:workspaceId/billing/invoices/:invoiceId/download-link | عضو لديه billing.read | رابط موقّع لملف PDF الخاص بالفاتورة، صالح خمس دقائق، مبني من PUBLIC_API_URL |
POST | /api/workspaces/:workspaceId/billing/preview | عضو لديه billing.manage | ما تكلفه الخطة planId (starter وpro وscale): ترقية أم تخفيض، ومتى يسري، ورسوم اليوم (chargeTodayCents)، والفاتورة التالية وأي blockers (المقاعد أو طلبات API أو التخزين فوق حدود الخطة الأصغر). ترقية اشتراك Stripe قائم يسعّرها Stripe ويجيب بـ chargesCardOnFile: true مع quote يثبّت ذلك السعر 30 دقيقة؛ أما الدفع التجريبي فيوزّع الفرق بالتناسب على بقية الفترة. الخطة الحالية تُجاب بـ 400 مع same_plan، إلا في أول دفعة (فترة تجريبية أو فترة منتهية أو مساحة عمل ملغاة)، حيث تُحصَّل أي خطة بسعرها الكامل |
POST | /api/workspaces/:workspaceId/billing/checkout | عضو لديه billing.manage | planId، وlocale اختياريًا، وquote المعاينة. ترقية اشتراك Stripe قائم تتم فورًا وتُحصَّل من وسيلة الدفع المحفوظة (upgraded: true) مع بقاء تاريخ التجديد كما هو؛ والتحصيل المرفوض يُجاب بـ 402 مع payment_declined أو payment_needs_action أو payment_method_missing، والـ quote المنتهي أو الغريب بـ 409 مع preview_expired. وأي ترقية أخرى تجيب بـ url صفحة الدفع المستضافة وsession. أما التخفيض فيُجدول للتجديد (scheduled: true)، وفي Stripe عبر جدول اشتراك، أو يُرفض بـ 409 مع over_plan_limit وblockers |
POST | /api/workspaces/:workspaceId/billing/checkout/:sessionId/sync | عضو لديه billing.read | بعد العودة من صفحة الدفع: تُقرأ الجلسة من المزود ويُجاب بالجلسة والملخص. لا تتغير الخطة إلا بعد أن يقول المزود إنها مدفوعة |
POST | /api/workspaces/:workspaceId/billing/keep-plan | عضو لديه billing.manage | التراجع عن تخفيض مجدول للتجديد؛ ويجيب بالملخص. وإذا لم يكن هناك شيء مجدول يُجاب بـ 409 مع no_scheduled_change |
POST | /api/workspaces/:workspaceId/billing/portal | عضو لديه billing.manage | url بوابة عملاء Stripe، لوسيلة الدفع؛ وurl: null لمساحة عمل لا تُفوتَر عبر Stripe |
POST | /api/workspaces/:workspaceId/billing/cancel | عضو لديه billing.manage | الإلغاء في نهاية الفترة. يُسقَط أي تخفيض مجدول للتجديد |
POST | /api/workspaces/:workspaceId/billing/resume | عضو لديه billing.manage | الإبقاء على اشتراك كان مضبوطًا للإلغاء |
PATCH | /api/workspaces/:workspaceId/billing/email | عضو لديه billing.manage | تغيير email الفوترة |
GET | /api/billing/invoices/download | أي شخص يملك الرابط الموقّع | ?token=: الفاتورة بصيغة PDF. الرمز الخاطئ يُجاب بـ 401 والمنتهي بـ 403 |
POST | /api/billing/webhooks/stripe | Stripe | أحداث Stripe، يُتحقق منها بـ STRIPE_WEBHOOK_SECRET مقابل الترويسة Stripe-Signature؛ التوقيع المفقود أو الخاطئ يُجاب بـ 400، وكذلك كل تسليم ما دام STRIPE_WEBHOOK_SECRET فارغًا، مع رسالة تذكره. تستخدم الاشتراكات checkout.session.completed وinvoice.paid وinvoice.payment_failed وcustomer.subscription.deleted، ويُطبَّق معرّف كل حدث مرة واحدة. وتصل طلبات التراخيص على المسار نفسه (الدفع لشراء التراخيص) |
GET | /api/billing/simulator/:sessionId | عضو لديه billing.manage في مساحة عمل الجلسة | جلسة الدفع التجريبي، لصفحة الدفع الخاصة بلوحة التحكم |
POST | /api/billing/simulator/:sessionId/complete | عضو لديه billing.manage في مساحة عمل الجلسة | دفع تجريبي: تتغير الخطة كما لو أكّد المزود الدفع |
POST | /api/billing/simulator/:sessionId/cancel | عضو لديه billing.manage في مساحة عمل الجلسة | ترك الدفع التجريبي دون دفع |
- يستخدم الدفع Stripe Checkout عندما يكون
STRIPE_SECRET_KEYمضبوطًا، والدفع التجريبي المدمج في غير ذلك، أو متى كانBILLING_PROVIDER=simulator. الدفع التجريبي لا يحصّل من أحد. - عنوان العودة وحده لا يغيّر الخطة أبدًا: يغيّرها فقط الـ webhook الموقّع، أو الجلسة المقروءة عند
sync. - يُنشأ سعر Stripe لكل خطة عند أول استخدام، بمفتاح البحث
northstack_<plan>_usd_<cents>_month، فلا يلزم إعداد أي شيء في Stripe أولًا. - إتمام دفع Stripe لمساحة عمل لديها اشتراك Stripe بالفعل يلغي القديم، مع احتساب وقته غير المستخدم رصيدًا. يخزّن
invoice.paidفاتورة Stripe مرة واحدة (الفواتير فريدة لكل معرّف فاتورة عند المزود)، مع مبلغها وفترتها، وينقل فترة مساحة العمل إلى الفترة التي تبدأ بها الفاتورة، وعند التجديد يطبّق التخفيض المجدول له. - في الدفع التجريبي تجدد واجهة API كل فترة عند نهايتها (فاتورة مدفوعة، و30 يومًا تالية، وتخفيض مجدول يُطبَّق) وتنهي ما ضُبط للإلغاء. والفترة التجريبية التي تبلغ نهايتها تصبح متأخرة في الدفع، مع مهلة السماح. وتبدأ عدادات الاستخدام من الصفر في كل فترة. يجري هذا عند كل قراءة للفوترة ومرة كل ساعة.
- وجّه webhook الخاص بـ Stripe إلى
https://<your API>/api/billing/webhooks/stripeمع الأحداث أعلاه إضافة إلىcheckout.session.expired.
مفاتيح API وواجهة API العامة
| الطريقة | المسار | من يحق له استدعاؤه | وظيفته |
|---|---|---|---|
GET | /api/workspaces/:workspaceId/api-keys | عضو لديه keys.manage | مفاتيح مساحة العمل: الاسم والبادئة والنطاقات والبيئة وآخر استخدام ومن أنشأها. من دون السر أبدًا |
POST | /api/workspaces/:workspaceId/api-keys | عضو لديه keys.manage | name (من 2 إلى 60) وscopes (واحد أو أكثر من projects:read وprojects:write وtasks:read وtasks:write) وenvironment (production، وهو الافتراضي، أو demo). يجيب بالمفتاح مع secret الذي يظهر هذه المرة فقط؛ ولا يُحفظ منه إلا تجزئة SHA-256 |
DELETE | /api/workspaces/:workspaceId/api-keys/:id | عضو لديه keys.manage | إلغاء مفتاح. يتوقف عن العمل فورًا |
GET | /api/v1/projects | مفتاح API لديه projects:read | أحدث 100 مشروع تحديثًا في مساحة العمل |
POST | /api/v1/projects | مفتاح API لديه projects:write | إنشاء مشروع، بالجسم نفسه الذي تستخدمه لوحة التحكم |
PATCH | /api/v1/projects/:projectId | مفتاح API لديه projects:write | تغيير مشروع، بالجسم نفسه الذي تستخدمه لوحة التحكم |
GET | /api/v1/projects/:projectId/tasks | مفتاح API لديه tasks:read | مهام مشروع واحد |
POST | /api/v1/projects/:projectId/tasks | مفتاح API لديه tasks:write | إنشاء مهمة، بالجسم نفسه الذي تستخدمه اللوحة |
curl http://localhost:8000/api/v1/projects \
-H "Authorization: Bearer nst_live_…"- يبدأ سر مفتاح الإنتاج بـ
nst_live_، ومفتاح العرض بـnst_demo_. ولا يعمل مفتاح العرض إلا ما دامت واجهة API في الوضع التجريبي. - من دون مفتاح، أو بمفتاح غير معروف أو ملغى، يُجاب بـ
401؛ والمفتاح الذي لا يملك نطاق المسار يُجاب بـ403؛ ومساحة العمل الموقوفة ترفض الكتابة. - كل مسار عدا قائمة المشاريع يعمل بصفة منشئ المفتاح: ما ينشئه يُسجَّل على أنه من صنعه، مع تحقق لوحة التحكم وأحداث النشاط وحدود الخطة. ويُجاب بـ
404متى غادر المنشئ مساحة العمل، وتُجاب عمليات الكتابة الثلاث بـ403متى لم يعد دور المنشئ يملكprojects.write. - كل استدعاء يُحتسب ضمن طلبات API الخاصة بالخطة، التي تقرؤها صفحة الفوترة وحدود الخطة.
نظرة عامة على المنصة والمؤسسات
| الطريقة | المسار | من يحق له استدعاؤه | وظيفته |
|---|---|---|---|
GET | /api/platform/overview | طاقم لديه platform.view | الإيرادات الشهرية المتكررة وتاريخها، وتوزيع الخطط، والمؤسسات بحسب الحالة. range (30d و90d و6m، وهو الافتراضي، و12m) |
GET | /api/platform/overview/export | طاقم لديه reports.export | كل المؤسسات بصيغة ملف CSV: organization وid وplan وstatus وseats وmrr_usd وcreated |
GET | /api/platform/organizations | طاقم لديه organizations.view | المؤسسات. search (الاسم أو العنوان) وplan (starter وpro وscale) وstatus (trialing وactive وpast_due وcanceling وcanceled وsuspended) وpage وlimit |
GET | /api/platform/organizations/:id | طاقم لديه organizations.view | مؤسسة واحدة، بمعرّفها العام |
POST | /api/platform/organizations/:id/suspend | طاقم لديه organizations.manage | reason (من 3 إلى 300 حرف). تحتفظ مساحة العمل بقراءاتها وترفض كل كتابة. والموقوفة أصلًا تُجاب بـ 409 |
POST | /api/platform/organizations/:id/reactivate | طاقم لديه organizations.manage | رفع الإيقاف |
المستخدمون والطاقم وأدوار المنصة والإعدادات
| الطريقة | المسار | من يحق له استدعاؤه | وظيفته |
|---|---|---|---|
GET | /api/users | طاقم لديه users.view | كل الحسابات. search وrole (owner أو admin أو member أو billing لدور مساحة عمل يشغله الحساب في أي مكان، وstaff لدور منصة، وnone للحسابات التي ليست في أي مساحة عمل) وpage وlimit |
GET | /api/users/deleted | طاقم لديه users.view | الحسابات المحذوفة، بالمرشّحات نفسها |
GET | /api/users/statistic | طاقم لديه users.view | الإجماليات: الحسابات، والمحذوفة، والطاقم، ومن بلا مساحة عمل، والجديدة في آخر 30 يومًا |
GET | /api/users/deleted/:username | طاقم لديه users.view | حساب محذوف واحد |
GET | /api/users/:username | طاقم لديه users.view | حساب واحد |
POST | /api/users | طاقم لديه users.create | إنشاء حساب: first_name وlast_name وemail وpassword (من 8 إلى 128)، وphone اختياريًا |
PATCH | /api/users/:username | طاقم لديه users.update | تغيير first_name أو last_name أو email أو phone. البريد الجديد يخضع للحماية الموضحة أدناه |
PATCH | /api/users/:username/change-password | طاقم لديه users.update | تعيين password جديدة (من 8 إلى 128)، وتخضع للحماية الموضحة أدناه |
DELETE | /api/users/:username | طاقم لديه users.delete | نقل حساب إلى قائمة المحذوفين. الحساب الذي يملك مساحة عمل يُجاب بـ 409 مع user_owns_workspace إلى أن تنتقل الملكية |
POST | /api/users/deleted/:username/restore | طاقم لديه users.restore | إعادة حساب محذوف |
GET | /api/admins | طاقم لديه admins.view | كل حساب مع أدوار المنصة. email وname (الاسم الأول أو الأخير أو اسم المستخدم) وphone، ويُرجَع الصف المطابق لأي واحد منها، وpage وpage_count |
GET | /api/admins/statistics | طاقم لديه admins.view | أعداد لصفحة الطاقم |
GET | /api/admins/:id | طاقم لديه admins.view | حساب واحد، بالمعرّف أو اسم المستخدم |
POST | /api/admins | طاقم لديه admins.create | إضافة حساب طاقم: first_name وlast_name وemail وpassword وpassword_confirmation (6 أحرف أو أكثر)، وphone وphone_code وprofile_picture اختياريًا |
PATCH | /api/admins/:id | طاقم لديه admins.edit | تغيير هذه الحقول؛ وكلمة المرور الجديدة أو البريد الجديد يخضعان للحماية الموضحة أدناه |
PATCH | /api/admins/:id/roles | طاقم لديه admins.assign_roles | تعيين أدوار المنصة للحساب: role_ids. كل دور يُمنح أو يُسحب يجب ألا يضم إلا صلاحيات يملكها المستدعي نفسه، وإلا يُجاب بـ 403 (role_outranks_caller). وتُبلَّغ لوحة التحكم المفتوحة لديه فورًا |
DELETE | /api/admins/:id | طاقم لديه admins.delete | إزالة حساب. الذي يملك مساحة عمل يُجاب بـ 409 |
PATCH | /api/admins/profile | طاقم لديه admins.edit | تغيير الملف الشخصي لعضو الطاقم المسجَّل دخوله |
GET | /api/roles | طاقم لديه roles.view | أدوار المنصة. name وguard_name وcreated_from وcreated_to وpage وpage_count |
GET | /api/roles/statistics | طاقم لديه roles.view | أعداد لصفحة الأدوار |
GET | /api/roles/select | طاقم لديه roles.view | معرّف كل دور واسمه، لقائمة الاختيار |
GET | /api/roles/permissions | طاقم لديه roles.view | كل صلاحيات المنصة، مجمّعة بحسب المجال |
GET | /api/roles/:id | طاقم لديه roles.view | دور واحد بصلاحياته |
POST | /api/roles | طاقم لديه roles.create | إضافة دور: name. الاسم المستخدم يُجاب بـ 409 |
PUT | /api/roles/:id | طاقم لديه roles.edit | إعادة تسمية دور: name |
POST | /api/roles/:id/permissions | طاقم لديه roles.assign_permissions | تعيين permissions الدور، بالاسم. لا يجوز للمستدعي إعطاء إلا الصلاحيات التي يملكها، ولا تغيير إلا دور لا يضم صلاحية ينقصه، وإلا يُجاب بـ 403 (permission_outranks_caller). ويُبلَّغ كل من يحمله فورًا |
DELETE | /api/roles/:id | طاقم لديه roles.delete | حذف دور. الدور الذي يحمله أحد يُجاب بـ 400 |
GET | /api/settings | طاقم لديه settings.view | إعدادات المنصة. search وcategory وpage وpage_count |
GET | /api/settings/:key | طاقم لديه settings.view | إعداد واحد |
PATCH | /api/settings/:key | طاقم لديه settings.edit | تغيير إعداد واحد: value |
DELETE | /api/settings/:key | طاقم لديه settings.edit | احذف إعدادًا واحدًا |
الحماية على كلمات المرور والبريد: لا يجوز لمستدعٍ غير الحساب نفسه تعيين كلمة مرور الحساب أو تغيير بريده إلا إذا كان مسؤول المنصة أو يملك كل صلاحية منصة يملكها الحساب؛ وإلا تُجيب واجهة API بـ 403. والبريد محمي أيضًا لأن رابط إعادة تعيين كلمة المرور يُرسل إليه.
الحماية على الأدوار: لا يستطيع أحد منح دور منصة أو سحبه أو رفعه فوق صلاحياته، فلا تؤدي admins.assign_roles أو roles.assign_permissions وحدها إلى دور مسؤول المنصة.
صلاحيات المنصة الـ 21 هي platform.view وreports.export وorganizations.view وorganizations.manage وusers.view وusers.create وusers.update وusers.delete وusers.restore وadmins.view وadmins.create وadmins.edit وadmins.delete وadmins.assign_roles وroles.view وroles.create وroles.edit وroles.delete وroles.assign_permissions وsettings.view وsettings.edit.
الإعدادات العامة ووضع الصيانة
| الطريقة | المسار | من يحق له استدعاؤه | وظيفته |
|---|---|---|---|
GET | /api/public-settings | أي شخص | maintenanceMode وtrialDays (قيمة trial_days للمنصة، و14 إذا لم تُضبط): ما تذكره التهيئة على أنه مدة الفترة التجريبية، وهل تعرض لوحة التحكم شاشة الصيانة. مفتوح أثناء الصيانة |
ما دام إعداد المنصة maintenance_mode بقيمة true، يُجاب كل طلب من أي شخص ليس من طاقم المنصة (حساب يملك platform.view) بـ 503، مع code: "maintenance_mode" والرسالة «المنصة مغلقة مؤقتًا للصيانة. يُرجى المحاولة مرة أخرى بعد قليل.» والرمز المفقود أو الخاطئ يحصل على 503 نفسها. وتبقى هذه مفتوحة للجميع:
GET /api/healthوGET /api/public-settings؛POST /api/auth/loginوPOST /api/auth/logout، حيث يرفض تسجيل الدخول أي شخص ليس من الطاقم بـ503حتى بعد صحة بيانات الدخول؛POST /api/billing/webhooks/stripeوتنزيل الفاتورة الموقّع؛- نقطة MCP، التي تحتفظ بمفتاحها الخاص، و
GET /api/demo/status؛ POST /api/contactالخاص بالموقع وكل مسار/api/licenses، فيستمر نموذج التواصل والدفع لشراء التراخيص في العمل.
كل ما عدا ذلك مغلق أمام من ليس من الطاقم، بما فيه التسجيل واستعادة الحساب والدعوات والتهيئة وواجهة API العامة تحت /api/v1 والملفات المرفوعة والمساعد. والقائمة في back-end/src/common/constants/maintenance.constants.ts.
نموذج التواصل
| الطريقة | المسار | من يحق له استدعاؤه | وظيفته |
|---|---|---|---|
POST | /api/contact | أي شخص | name (من 2 إلى 120) وemail وtopic (licensing وsetup وproduct) وmessage (من 20 إلى 4000 حرف) وclientToken (من 8 إلى 64 من الأحرف والأرقام و_ أو -) والحقل المخفي website. يجيب بـ { id } |
clientTokenالمكرر يعيد معرّف الرسالة الأولى بدلًا من تخزين ثانية، فإعادة المحاولة آمنة.- الحقل
websiteالمملوء يُعامل على أنه روبوت: تبدو الإجابة نفسها، ولا يُخزَّن شيء. - يستطيع كل عنوان إرسال خمس رسائل في الساعة؛ والتالية تُجاب بـ
429. - تُخزَّن الرسائل في جدول
contact_messagesوتُحوَّل إلىCONTACT_INBOXإذا كان مضبوطًا.
الدفع بالترخيص
| الطريقة | المسار | من يحق له استدعاؤه | وظيفته |
|---|---|---|---|
GET | /api/licenses/checkout/options | أي شخص | وسائل الدفع (card وpaypal)، وprovider الذي يتولى كل واحدة (stripe أو paypal أو simulator)، وtestMode، وقيمته true عندما لا يحرّك أي منهما مالًا حقيقيًا |
POST | /api/licenses/checkout | أي شخص | license (single وstudio) وemail وname (من 2 إلى 120) وmethod (card وpaypal)، وlocale اختياريًا (en وar)، وclientToken. يجيب بـ order، معرّف الطلب، وurl، صفحة الدفع. وصفحة الدفع التي لا تُفتح تُجاب بـ 503 مع payment_unavailable |
GET | /api/licenses/orders/:id | أي شخص يملك معرّف الطلب | الطلب: المرجع والترخيص والمبلغ والبريد والاسم والوسيلة والمزود، وstatus (pending وpaid وcancelled وfailed) وtest، وlicenseKey بعد أن يُدفع |
POST | /api/licenses/orders/:id/confirm | أي شخص يملك معرّف الطلب | عاد المشتري من Stripe أو PayPal: يُسأل المزود هل تمت الدفعة، ثم يُجاب بالطلب |
POST | /api/licenses/orders/:id/cancel | أي شخص يملك معرّف الطلب | غادر المشتري صفحة الدفع دون أن يدفع |
POST | /api/licenses/orders/:id/simulate | أي شخص يملك معرّف الطلب | زر الدفع أو الإلغاء في الدفع التجريبي: outcome (pay وcancel). الطلب الذي تولاه Stripe أو PayPal يُجاب بـ 404 |
- السعر هو سعر الخادم نفسه، من
back-end/src/modules/licenses/constants/license-offers.ts؛ ولا يرسل المتصفح سعرًا أبدًا. - تذهب البطاقة إلى Stripe Checkout عندما يكون
STRIPE_SECRET_KEYمضبوطًا، وPayPal إلى PayPal عندما يكونPAYPAL_CLIENT_IDوPAYPAL_CLIENT_SECRETمضبوطين، وكلتاهما إلى الدفع التجريبي في غير ذلك. وفي الوضع التجريبي لا يُعرض أبدًا مزود بمفاتيح حقيقية. - يُدفع الطلب مرة واحدة، بأيٍّ يأتي أولًا من
checkout.session.completedفي Stripe علىPOST /api/billing/webhooks/stripe، أوconfirmعند العودة، أو زر الدفع في الدفع التجريبي. وهذا وحده يصدر مفتاح الترخيص ويرسل الإيصال بالبريد. أماcheckout.session.expiredفيعلّم الطلب بأنه فاشل. - معرّف الطلب سلسلة عشوائية طويلة تحملها روابط عودة المشتري فقط.
clientTokenالمكرر يُجاب بالطلب الأول. يستطيع كل عنوان بدء عشرة طلبات في الساعة.
الملفات المرفوعة والوسائط
| الطريقة | المسار | من يحق له استدعاؤه | وظيفته |
|---|---|---|---|
POST | /api/helpers/upload | أي حساب مسجّل الدخول | تخزين صورة: file بصيغة multipart (10 ميغابايت على الأكثر)، وpath للمجلد، وfor للمقاس المعدّ مسبقًا (profile أو cover أو logo أو default). يجيب بالعناوين بحسب المقاس: original، بعرض لا يتجاوز 1920 بكسل، ونسخة واحدة مصغّرة، كلها JPEG. الملف الذي ليس صورة مقروءة يُجاب بـ 400 |
تذهب الصور وملفات المشاريع إلى حاوية Cloudflare R2 عندما تُضبط متغيراتها الخمسة R2_*، وإلى MEDIA_UPLOAD_DIR في غير ذلك، وتُقدَّم على PUBLIC_MEDIA_URL متبوعًا بـ /uploads/….
كل ما تقدمه واجهة API تحت /media يحمل X-Content-Type-Options: nosniff، والملف الذي ليس صورة نقطية أو PDF أو نصًا عاديًا أو صوتًا أو فيديو (مثل ملف HTML أو SVG) يُرسل مع Content-Disposition: attachment، فيُنزَّل بدلًا من أن يُفتح صفحةً على عنوان واجهة API. أما الملفات في حاوية R2 فتقدمها الحاوية، بالنوع الذي رُفعت به.
الحدود لكل عنوان
| المسار | الحد | المتغير |
|---|---|---|
POST /api/auth/login | 10 محاولات دخول فاشلة كل 15 دقيقة | RATE_LIMIT_LOGIN |
POST /api/auth/signup | 10 في الساعة | لا شيء |
POST /api/auth/forgot-password | 5 كل 15 دقيقة | لا شيء |
POST /api/auth/reset-password | 10 كل 15 دقيقة | لا شيء |
POST /api/contact | 5 في الساعة | لا شيء |
POST /api/licenses/checkout | 10 في الساعة | لا شيء |
POST …/invitations/:id/resend | مرة في الدقيقة، وخمس مرات لكل دعوة | لا شيء |
الطلب الذي يتجاوز حدًّا يُجاب بـ 429، مع جملة تذكر ما حُدّ وكم يجب الانتظار، مثل «طلبات تسجيل كثيرة من هذا العنوان. انتظر ساعة ثم حاول مرة أخرى.» تعيش العدادات في ذاكرة واجهة API، فيمحوها إعادة التشغيل. خلف وكيل عكسي، اضبط TRUST_PROXY على عدد الوكلاء أمام واجهة API، وإلا يُحتسب كل زائر على أنه عنوان الوكيل الواحد.
المساعد الذكي ومحادثاته واقتراحاته
مُضمَّن مع مشترياتك. سجّل الدخول لقراءته، أو افتحه في ملف التنزيل.
مسار البث للمساعد، وقائمة نماذجه، والمحادثات المحفوظة، واقتراحات المهام.
خادم MCP
مُضمَّن مع مشترياتك. سجّل الدخول لقراءته، أو افتحه في ملف التنزيل.
نقطة MCP لوكلاء البرمجة، وكيف يُصرَّح لها، وأي مساحة عمل تقرؤها.
تحديثات الصلاحيات الحية عبر socket.io
مُضمَّن مع مشترياتك. سجّل الدخول لقراءته، أو افتحه في ملف التنزيل.
فضاء أسماء socket.io الذي تستمع إليه لوحة التحكم، وحدثه، وكيف يُصرَّح للاتصال.
مسارات وضع العرض التجريبي
مُضمَّن مع مشترياتك. سجّل الدخول لقراءته، أو افتحه في ملف التنزيل.
المسارات الموجودة فقط لبناء العرض التجريبي العام.