البدء السريع
أنشئ من لوحة إدارة Web تطبيقاً خارجياً مرتبطاً بالشركة المطلوبة، ثم انسخ App ID وApp Secret. أضف Callback URL اختيارياً إذا كنت تحتاج النتيجة النهائية.
ينشئ Engine سجلات للمستخدمين الذين يحددهم audience الاختياري (كل مستخدمي الشركة المؤهلين افتراضياً)، ويحاول Push النظام فقط للأجهزة المسجلة المؤهلة.
نقطة النهاية والمصادقة
بيئات API:
- الاختبار: https://aim-api-test.proton-system.com
- الإنتاج: https://aim-api.proton-system.com
يجب أن يتضمن كل طلب JSON بين الخوادم Authorization: Basic Base64(KEY:SECRET) باستخدام HTTP Basic Authentication، حيث يكون KEY هو App ID وSECRET هو App Secret:
Endpoint: POST /openapi/v1/notifications.
curl --request POST 'https://<your-engine-host>/openapi/v1/notifications' \
--header 'Authorization: Basic <Base64(AppID:AppSecret)>' \
--header 'Content-Type: application/json' \
--data '{"requestId":"order-20260819-001","title":"اكتمل الطلب","body":"تم إكمال طلبك.","imageUrls":["https://cdn.example.com/order/001-1.webp","https://cdn.example.com/order/001-2.webp"],"displayFields":[{"label":"اسم المتجر","value":"مطعم شياو هان للنودلز (وانغجينغ)"},{"value":"ORD-10086"}],"tags":["مكتمل","الفرع الرئيسي"]}'عقد الطلب
ملخص الحقول
| الحقل | إلزامي | النوع | المعنى |
|---|---|---|---|
requestId | نعم | string | معرّف طلب فريد من المرسل ومفتاح idempotency؛ يُقصّ ويجب ألا يكون فارغاً. |
title | نعم | string | عنوان الإشعار؛ يُقصّ ويجب ألا يكون فارغاً. |
body | نعم | string | نص الإشعار؛ يُقصّ ويجب ألا يكون فارغاً. |
imageUrls | لا | string[] | صور التفاصيل بترتيب العرض. |
displayFields | لا | object[] | حقول نصية إضافية مرتبة في التفاصيل. |
tags | لا | string[] | علامات plain text تظهر في التفاصيل فقط وبالترتيب. |
audience | لا | object | يقيّد الإرسال بالمستخدمين أو الأدوار؛ حذفه يعني كل مستخدمي الشركة المؤهلين. |
الصور: imageUrls
- احذف الحقل أو أرسل
nullأو[]عند عدم وجود صور؛ وإلا فأرسل 1-9 عناوين بالترتيب. - تُقص المسافات من كل URL، ويجب أن يكون فريداً وعنوان
HTTPSمطلقاً بلا اسم مستخدم أو كلمة مرور. - الحد 2048 حرف Unicode لكل URL. عنوان واحد غير صالح يرفض الطلب بالكامل.
- يخزن Engine العناوين ولا يجلب الصور أو يفحصها أو يعمل وكيلاً لها أو يخزنها مؤقتاً.
حقول التفاصيل: displayFields
| الحقل الفرعي | إلزامي | الحد | العرض |
|---|---|---|---|
label | لا | غير فارغ بعد القص، حتى 32 Unicode حرفاً | اسم الحقل. |
value | نعم | غير فارغ بعد القص، حتى 256 Unicode حرفاً | قيمة الحقل. |
{
"displayFields": [
{ "label": "المتجر", "value": "Little Han Noodles (Wangjing)" },
{ "value": "ORD-10086" }
]
}- حذف الحقل أو إرسال
nullأو[]يعني عدم وجود صفوف إضافية؛ الحد 10 كائنات. - عند حذف
labelأو إرساله بقيمةnullيظهرvalueفي سطر كامل العرض. - المحتوى دائماً plain text؛ لا تُدعم HTML أو Markdown أو الروابط أو البيانات المتداخلة أو الأنواع أو الأنماط.
علامات التفاصيل tags
- حذف الحقل أو إرسال
nullأو[]يعني عدم وجود علامات؛ وإلا فأرسل 8 strings كحد أقصى بترتيب العرض. - تُقص كل علامة ويجب أن تحتوي 1-20 حرف Unicode. القيمة الفارغة أو غير string أو الطويلة أو المكررة بعد القص ترفض الطلب كله بـ
INVALID_REQUEST. - العلامات plain text غير تفاعلية، تظهر في تفاصيل الإشعار فقط وتحافظ على ترتيبها.
المستلمون: audience
type | المحدد | معنى ID |
|---|---|---|
USERS | userIds | معرف عضو الشركة |
ROLES | roles | CompanyRole ID؛ يستلم الأعضاء المؤهلون في أي دور محدد. |
انسخ معرف عضو الشركة من قائمة أعضاء الشركة في Web Admin. يجب أن ينتمي العضو إلى الشركة المرتبطة بتطبيق الطرف الثالث.
{
"audience": { "type": "USERS", "userIds": ["companyMemberId"] }
}{
"audience": { "type": "ROLES", "roles": ["roleId"] }
}- حذف
audienceأو إرسالnullيستهدف كل المستخدمين النشطين المؤهلين في الشركة المرتبطة. - تحتوي القائمة على 1-100 string خام؛ تُقص المعرّفات، وتُهمل الفارغة، وتُزال التكرارات وتُرتب كمجموعة.
- تُهمل المعرّفات غير الموجودة أو المعطلة أو المحذوفة أو التابعة لشركة أخرى؛ ويستلم باقي الأهداف الصالحة.
- يُقبل المحدد بلا أهداف وينتهي بـ
recipientCount=0؛ ولا يعود إلى الإرسال لكل الشركة. - يمكن حذف المحدد الآخر أو جعله
null. أما الخلط غير null أو type/field غير معروف أو القائمة المفقودة أو الفارغة أو العنصر غير string أو أكثر من 100 عنصر فيعيدINVALID_REQUEST.
الحدود وidempotency
- الحد الأقصى للـ body هو 256 KiB. حدود
requestIdوtitleوbodyهي 128 و100 و1000 حرف Unicode. - لكل App ID حد 10 طلبات في الدقيقة و100 في الساعة.
- يعيد
requestIdنفسه مع محتوى مطبّع مكافئ النتيجة المحفوظة. ترتيب الصور والحقول وtags مهم؛ ترتيب audience وتكراره غير مهم. - المحتوى المختلف يعيد
IDEMPOTENCY_CONFLICT. قد تكونstatusالنهائيةSUCCESSأوPARTIAL_SUCCESSأوFAILED.
استجابة القبول
يعيد الطلب الصحيح HTTP 202:
{
"requestId": "order-20260819-001",
"notificationId": "00000000-0000-0000-0000-000000000001",
"status": "ACCEPTED"
}تعني ACCEPTED أن Engine حفظ الطلب للتوزيع فقط، ولا تؤكد أن APNs/FCM أو الجهاز عرض الإشعار.
نتائج Callback
عند إعداد Callback URL يرسل Engine النتيجة النهائية:
{
"requestId": "order-20260819-001",
"status": "PARTIAL_SUCCESS",
"recipientCount": 8,
"pushAcceptedCount": 1,
"pushFailedCount": 1
}recipientCount هو عدد مستخدمي مركز الإشعارات المستلمين، بينما يمثل pushAcceptedCount وpushFailedCount عدد دفعات Push Outbox، وليس عدد المستخدمين أو الأجهزة ولا دليلاً على ظهور إشعار النظام.
اختبار Callback
يرسل كل اختبار Callback من لوحة الإدارة حدث فحص مستقلاً:
{
"requestId": "callback-test-<unique-id>",
"status": "SUCCESS",
"recipientCount": 1,
"pushAcceptedCount": 1,
"pushFailedCount": 0,
"test": true
}عندما تكون test بالقيمة true، تحقق من البنية وأعد 2xx، لكن لا تربط حدث الفحص بطلب إشعار مرسل ولا تحدّث حالة التسليم التجارية. قيم العد ثابتة للاختبار ولا تمثل مستلمين حقيقيين أو دفعات Push Outbox فعلية.
تكون قيمة status النهائية SUCCESS أو PARTIAL_SUCCESS أو FAILED. أعد أي استجابة 2xx للتأكيد.
وإلا يعيد Engine المحاولة بعد دقيقة و5 دقائق و30 دقيقة وساعتين و6 ساعات و24 ساعة. لا يوجد توقيع HMAC حالياً: استخدم HTTPS، وطابق requestId للنتائج غير الاختبارية، وقيّد الوصول، وعالج Callback بطريقة idempotent.
مرجع الأخطاء
| HTTP | Code | المعنى |
|---|---|---|
| 400 | INVALID_REQUEST | خطأ في Content-Type أو JSON أو الحجم أو الحقول. |
| 401 | INVALID_APP_CREDENTIALS | App ID أو App Secret غير صحيح. |
| 403 | APPLICATION_INACTIVE | التطبيق أو الشركة غير نشط. |
| 409 | IDEMPOTENCY_CONFLICT | أُعيد استخدام requestId بمحتوى مختلف. |
| 429 | RATE_LIMITED | تم تجاوز حد التطبيق. |
| 503 | NOTIFICATION_UNAVAILABLE | الخدمة غير متاحة مؤقتاً. |
الأمان ونطاق الإرسال
- App ID وApp Secret بيانات اعتماد بين الخوادم. لا تضعها في المتصفح أو تطبيق الهاتف أو مستودع عام أو URL أو تخزين العميل. أعد تعيين App Secret عند التسرب.
- لا تُكتب معرّفات الأهداف الخاصة مطلقاً في logs التطبيق، ولا يظهر selector الخاص أو معرّفاته في GraphQL أو Push أو Callback أو realtime أو navigation.
- من دون
audienceيُضم جميع المستخدمين النشطين المؤهلين في الشركة، ومع audience تُضم الأهداف الصالحة فقط. يحصل كل مستلم صالح على سجل ويُحتسب ضمنrecipientCount. - لا تتم محاولة Push النظام إلا للأجهزة التي لديها
PushInstallationنشط يسمح بفئةBUSINESS، من دون ضمان ظهور شريط النظام على كل جهاز. - تظل قائمة
imageUrlsالكاملة متاحة فقط في تفاصيل الإشعار المصرح بها، لكن قد يرسل Engine أول URL فيimageUrlsإلى APNs/FCM لعرض صورة شريط النظام.displayFieldsوtagsلا يدخلان في Push أو Callback، وهما plain text غير تفاعلي؛ لا تضع فيهما أسراراً أو رموز وصول أو بيانات دفع أو قيماً حساسة. - يجب أن يظل
titleوbodyكاملين ومفهومين من دون الصور. لا يؤدي فشل تنزيل الصور أو عرضها إلى تغيير حالة accepted أو Callback أو Push. - يقبل Mobile client حداً أقصى قدره 5 MiB لكل صورة، وتنتهي مهلة عملية تنزيل الصورة كاملة بعد 10 ثوانٍ. لا يتبع العميل عمليات إعادة التوجيه 3xx، لذا يجب أن يعيد كل URL الصورة مباشرة.
- ينزّل تطبيق الهاتف كل صورة مباشرة من CDN العام؛ استخدم عناوين لا تتطلب مصادقة أو ملفات Cookie، وأبقها متاحة 90 يوماً على الأقل، مع مراعاة أن CDN يستقبل IP المستخدم ووقت الطلب وUser-Agent.
- استخدم عناوين كائنات غير قابلة للتغيير عندما يلزم ثبات العرض التاريخي.