API Reference · v1

أرسل إشعارات لعملاء تطبيقك — بطلب واحد

مفتاح عام داخل التطبيق لتسجيل الأجهزة، ومفتاح سري في خادمك للإرسال. نحن نتولى Apple و Google، والتوجيه، وإعادة المحاولة، وتنظيف الأجهزة الميتة.

ابدأ في 3 خطوات
Introduction

مقدمة

KD Push خدمة إشعارات مُدارة بالكامل للمنصات وتجارها. التاجر لا يتعامل مع OneSignal أو Firebase أو شهادات Apple: يضيف تطبيقه في البوابة، ويأخذ App Key و Server Key، ومنصته ترسل عبر هذه الواجهة.

Your platform
→
KD Push API
→
Queue & engine
→
APNs / FCM
→
Customers' devices

كل الطلبات والردود بصيغة JSON عبر HTTPS، والتواريخ بصيغة ISO 8601، والمعرّفات العامة بصيغة ULID.

Quick start

البدء السريع

  1. 1
    أنشئ تطبيقك وخذ المفاتيح
    من بوابة التاجر: تطبيق جديد ← انسخ App Key و Server Key (يظهر مرة واحدة).
  2. 2
    سجّل الجهاز من التطبيق
    cURL
    curl -X POST https://push.kd-app.com/api/v1/sdk/devices \
      -H "X-KD-App-Key: kdp_app_…" \
      -H "Content-Type: application/json" \
      -d '{
        "platform": "android",
        "token": "fcm-registration-token",
        "installation_id": "3f0c9a2e-6b1d-4c8e-9f7a-2d5e8b1c4a90",
        "android": { "fcm_project_id": "neon-push" },
        "external_user_id": "42",
        "user_signature": "9f2c4e…",
        "locale": "ar",
        "app_version": "2.1.0"
      }'
  3. 3
    أرسل من خادمك
    cURL
    curl -X POST https://push.kd-app.com/api/v1/messages \
      -H "Authorization: Bearer kdp_live_…" \
      -H "Content-Type: application/json" \
      -H "Idempotency-Key: app-created-1234" \
      -d '{
        "title": "تطبيق جديد",
        "body": "تمت إضافة واتساب الذهبي للمتجر",
        "url": "neonstore://app/1234",
        "image_url": "https://cdn.example.com/icons/1234.png",
        "data": { "app_id": "1234" },
        "audience": { "type": "all" }
      }'
Authentication

المصادقة

لكل تطبيق نوعان من المفاتيح، ولكل واحد مكان واحد فقط:

App Key
kdp_app_…
X-KD-App-Key: kdp_app_…
مكانه:
داخل تطبيق الجوال (عام)
يستطيع:
تسجيل الأجهزة، قراءة الإعدادات، تسجيل الفتح
لا يستطيع الإرسال أبداً
Server Key
kdp_live_…
Authorization: Bearer kdp_live_…
مكانه:
في خادم منصتك فقط (سري)
يستطيع:
الإرسال، المتابعة، الإلغاء، الإحصائيات
يظهر مرة واحدة — يُخزَّن عندنا كـ hash
المفتاح هو الذي يحدد التطبيق. لا توجد نقطة تقبل معرّف تطبيق أو تاجر في جسم الطلب، فلا يمكن لمفتاح تطبيق أن يصل لأجهزة أو إشعارات تطبيق آخر — الطلب يرجع 404.
Errors

الأخطاء

كل الأخطاء بنفس الشكل، ولا تحتوي تفاصيل داخلية:

422 Unprocessable Entity
{
  "error": {
    "code": "validation_failed",
    "message": "The body field is required.",
    "fields": {
      "body": [
        "The body field is required."
      ]
    }
  }
}
401invalid_app_key
مفتاح التطبيق غير صحيح أو التطبيق معطّل.
401invalid_server_key
مفتاح الخادم غير صحيح أو ملغى.
402subscription_inactive
لا يوجد اشتراك فعّال للتاجر.
402quota_exceeded
انتهت حصة الإشعارات الشهرية في الباقة.
403invalid_user_signature
التوقيع لا يطابق external_user_id.
403web_disabled · origin_not_allowed
الويب متوقف في التطبيق، أو الصفحة ليست على أحد مواقعه.
422invalid_subscription · provider_mismatch
مفاتيح اشتراك المتصفح غير صالحة، أو منصة لا تطابق المزوّد.
403app_disabled · account_banned
التطبيق معطّل أو الحساب محظور.
404not_found
غير موجود — أو يتبع تطبيقاً آخر.
409not_cancelable
الإشعار في حالة نهائية.
422validation_failed
بيانات غير صحيحة؛ التفاصيل في fields.
422invalid_token
توكن الجهاز بصيغة غير صحيحة.
429rate_limited
تجاوزت الحد؛ انتظر حسب Retry-After.
500server_error
خطأ داخلي — أعد المحاولة لاحقاً.
Rate limits

حدود الاستخدام

60
طلب/دقيقة
نقاط التطبيق لكل IP
20,000
طلب/دقيقة
نقاط التطبيق لكل تطبيق
600
طلب/دقيقة
نقاط الخادم لكل Server Key

عند التجاوز تحصل على 429 rate_limited مع ترويسة Retry-After. الإرسال لعدد كبير من الأجهزة طلب واحد — المحرك يقسمه داخلياً.

Idempotency

منع الإرسال المكرر

أرسل ترويسة Idempotency-Key مع كل طلب إرسال (مثلاً معرّف الحدث عندك). إعادة نفس الطلب بنفس المفتاح خلال 24 ساعة تعيد نفس الإشعار بـ 200 بدل إنشاء إشعار جديد — فإعادة المحاولة بعد انقطاع الشبكة آمنة دائماً.

Header
Idempotency-Key: app-created-1234-v2.1.0
Mobile app

إعدادات التشغيل

GET/sdk/configApp Key — تطبيق الجوال

اطلبها عند فتح التطبيق. أندرويد يأخذ منها خيارات Firebase، فتغيير مشروع Firebase أو المزوّد لا يحتاج إعادة بناء التطبيق.

يمكن تخزينها مؤقتاً — الرد يحمل Cache-Control: max-age=300.
Request
curl https://push.kd-app.com/api/v1/sdk/config \
  -H "X-KD-App-Key: kdp_app_…"
200 OK
{
  "app": {
    "id": "01JB2Q6V8K4Y3N5M7R9T1W0XZC",
    "name": "My Store"
  },
  "identity_verification": "required",
  "android": {
    "provider": "fcm",
    "firebase": {
      "project_id": "neon-push",
      "app_id": "1:123456789:android:abcdef",
      "api_key": "AIza…",
      "sender_id": "123456789",
      "storage_bucket": null
    }
  },
  "ios": {
    "provider": "apns"
  },
  "web": {
    "provider": "webpush",
    "vapid_public_key": "BEl62iUYgUivxIkv69yViEuiBIa-Ib9-SkvMeAtA3LFgDzkrxZJjSgSnfckjBJuBkr3qBUYIHBQFLXYp5Nksh8U",
    "icon_url": null,
    "prompt": {
      "enabled": true,
      "delay_seconds": 6,
      "title": null,
      "text": null
    }
  }
}
Mobile app

تسجيل الجهاز

POST/sdk/devicesApp Key — تطبيق الجوال

استدعه عند كل فتح للتطبيق، وعند تجدد التوكن، وعند تسجيل الدخول والخروج. أول مرة يرجع 201 وبعدها 200.

الحقلالوصف
platformios | android | webإلزامي
منصة الجهاز.
tokenstringإلزامي
توكن APNs (hex) أو FCM، وللمتصفح عنوان الاشتراك (endpoint).
installation_iduuidاختياري
معرّف ثابت تولّده مرة وتحفظه في التطبيق لكل App Key. يربط تحديثات التوكن بنفس الجهاز.
providerapns | fcm | webpushاختياري
يُستنتج من المنصة إن لم يُرسل.
ios.bundle_idstringاختياري
قيمة Bundle.main.bundleIdentifier.
ios.profile_app_idstringاختياري
application-identifier من embedded.mobileprovision بدون بادئة الفريق.
ios.team_idstring(10)اختياري
TeamIdentifier من نفس الملف.
ios.environmentproduction | sandboxاختياري
قيمة aps-environment.
android.fcm_project_idstringاختياري
مشروع Firebase الذي أصدر التوكن (من /sdk/config).
web.p256dh · web.authbase64urlحسب الحالة
مفتاحا اشتراك المتصفح (من PushSubscription.toJSON()). مطلوبان للويب، ويُقبل الطلب فقط من صفحة على أحد مواقع التطبيق.
external_user_idstring | nullاختياري
معرّف المستخدم عندك. أرسل null عند تسجيل الخروج.
user_signaturehexحسب الحالة
إلزامي مع external_user_id طالما التحقق من الهوية مفعّل. كيف تحسبه
tagsobjectاختياري
حتى 20 وسماً للاستهداف، المفاتيح [A-Za-z0-9_.-] والقيم نصوص قصيرة.
locale · timezone · app_version · os_version · device_model · sdk_versionstringاختياري
معلومات تظهر في لوحة التاجر.
التوكن ينتمي لتطبيق واحد فقط. إذا سجّله تطبيق آخر (مثلاً تثبيت APK متجر آخر فوق متجرك) ينتقل الجهاز إليه وتُحذف بيانات المستخدم والوسوم القديمة — فلا يصل إشعارك لعميل متجر آخر.
Request
curl -X POST https://push.kd-app.com/api/v1/sdk/devices \
  -H "X-KD-App-Key: kdp_app_…" \
  -H "Content-Type: application/json" \
  -d '{
    "platform": "android",
    "token": "fcm-registration-token",
    "installation_id": "3f0c9a2e-6b1d-4c8e-9f7a-2d5e8b1c4a90",
    "android": { "fcm_project_id": "neon-push" },
    "external_user_id": "42",
    "user_signature": "9f2c4e…",
    "locale": "ar",
    "app_version": "2.1.0"
  }'
201 Created
{
  "device": {
    "installation_id": "3f0c9a2e-6b1d-4c8e-9f7a-2d5e8b1c4a90",
    "platform": "android",
    "status": "active",
    "external_user_id": "42"
  }
}
403 Forbidden
{
  "error": {
    "code": "invalid_user_signature",
    "message": "user_signature does not match external_user_id."
  }
}
Mobile app

إلغاء الاشتراك

POST/sdk/devices/unsubscribeApp Key — تطبيق الجوال

عندما يطفئ المستخدم الإشعارات من داخل التطبيق. يتوقف الإرسال للجهاز حتى يسجّل مرة أخرى.

الحقلالوصف
installation_iduuidإلزامي
نفس المعرّف المستخدم في التسجيل.
Request
curl -X POST https://push.kd-app.com/api/v1/sdk/devices/unsubscribe \
  -H "X-KD-App-Key: kdp_app_…" \
  -H "Content-Type: application/json" \
  -d '{ "installation_id": "3f0c9a2e-6b1d-4c8e-9f7a-2d5e8b1c4a90" }'
200 OK
{
  "unsubscribed": true
}
Mobile app

تسجيل فتح الإشعار

POST/sdk/eventsApp Key — تطبيق الجوال

عند ضغط المستخدم على الإشعار. يُحسب مرة واحدة لكل جهاز وإشعار، ويظهر في عدّاد "فُتح".

الحقلالوصف
type"opened"إلزامي
نوع الحدث.
message_idULIDإلزامي
قيمة kd_message_id من الإشعار.
installation_iduuidإلزامي
معرّف الجهاز.
Request
curl -X POST https://push.kd-app.com/api/v1/sdk/events \
  -H "X-KD-App-Key: kdp_app_…" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "opened",
    "message_id": "01JB2QA4N7C2X9V5B8M3K6P1RD",
    "installation_id": "3f0c9a2e-6b1d-4c8e-9f7a-2d5e8b1c4a90"
  }'
202 Accepted
{
  "accepted": true
}
Web

إشعارات الموقع (المتصفحات)

نفس الإشعارات تصل إلى زوار موقعك من المتصفح، بدون تطبيق وبدون Firebase أو شهادات: لكل تطبيق مفتاح VAPID خاص به تنشئه KD Push تلقائياً. تعمل في Chrome وEdge وFirefox وSamsung Internet وSafari على الماك، وعلى الآيفون والآيباد بعد إضافة الموقع إلى الشاشة الرئيسية (iOS 16.4 فأحدث).

  1. في بوابة التاجر: التطبيق ← الويب، فعّل إشعارات الموقع وأضف دومينك (أو من خادمك عبر PUT /app/web).
  2. أنشئ الملف /kdpush-sw.js في جذر موقعك بهذا السطر:
Node.js
importScripts('https://push.kd-app.com/sdk/web/v1/kdpush-sw.js');
  1. أضف سكربت الـ SDK في كل الصفحات قبل </head>:
Text
<script src="https://push.kd-app.com/sdk/web/v1/kdpush.js"
        data-app-key="kdp_app_…" async></script>

هذا كل شيء: بطاقة صغيرة تطلب من الزائر تفعيل الإشعارات (نصها ومدتها من البوابة)، ثم يُسجَّل المتصفح تلقائياً، ويعود ليتحقق مرة يومياً. الضغط على الإشعار يفتح رابطه ويُسجَّل في عدّاد "فُتح".

خيارات وواجهة برمجية

Node.js
<!-- data-* options -->
data-app-key        required
data-service-worker /kdpush-sw.js (default)
data-prompt         "off" hides the built-in card
data-lang           ar | en | es | fr | de (default: the page language)
data-user-id        your user id   + data-user-signature (identity verification)
data-tags           JSON, e.g. '{"plan":"vip"}'

// JavaScript
KDPush.subscribe()            // ask (from a click) and subscribe → "subscribed" | "denied" | "default" | "unsupported" | "disabled"
KDPush.unsubscribe()
KDPush.isSubscribed()         // Promise<boolean>
KDPush.login('42', signature) // link the browser to your user
KDPush.logout()
KDPush.setTags({ plan: 'vip' })
كل متصفح يُرسل الوسم os تلقائياً (ios · android · desktop)، فتستطيع مثلاً إرسال إعلان تطبيق آيفون لزوار الموقع من الآيفون فقط: {"platforms":["web"],"audience":{"type":"tags","tags":{"os":"ios"}}}. وإن كان لموقعك Service Worker خاص، أضف سطر importScripts إليه واجعل data-service-worker مساره.
Web

إعدادات الويب

PUT/app/webServer Key — خادمك

لمنصة تربط مواقع تجارها: فعّل الويب وحدّد الدومينات عند ربط المتجر أو عند تغيّر الدومين. التفعيل يحتاج دوميناً واحداً على الأقل وينشئ مفتاح VAPID للتطبيق. الحقول غير المرسلة تبقى كما هي.

الحقلالوصف
enabledbooleanإلزامي
تشغيل أو إيقاف الويب.
originsstring[] ≤ 20حسب الحالة
الدومينات المسموح لها بتسجيل الزوار، مثل shop.example.com (تُحفظ https://…).
icon_urlhttps URLاختياري
أيقونة كل إشعار.
default_urlURLاختياري
الصفحة عند الضغط إذا لم يحمل الإشعار رابط ويب.
prompt{enabled, delay_seconds, title, text}اختياري
بطاقة طلب الإذن.
Request
curl -X PUT https://push.kd-app.com/api/v1/app/web \
  -H "Authorization: Bearer kdp_live_…" \
  -H "Content-Type: application/json" \
  -d '{"enabled":true,"origins":["shop.example.com"],"icon_url":"https://cdn.example.com/logo.png"}'
200 OK
{
  "web": {
    "enabled": true,
    "origins": [
      "https://shop.example.com",
      "https://www.shop.example.com"
    ],
    "vapid_public_key": "BEl62iUYgUivxIkv69yViEuiBIa-Ib9-SkvMeAtA3LFgDzkrxZJjSgSnfckjBJuBkr3qBUYIHBQFLXYp5Nksh8U",
    "subscribers": 0,
    "icon_url": "https://cdn.example.com/logo.png",
    "default_url": null,
    "prompt": {
      "enabled": true,
      "delay_seconds": 6,
      "title": null,
      "text": null
    }
  }
}
Platform backend

إرسال إشعار

POST/messagesServer Key — خادمك

يُرسل (أو يُجدول) إشعاراً لأجهزة تطبيقك فقط. الرد فوري بحالة queued، والإرسال يتم في الخلفية — تابع الحالة والعدّادات عبر حالة الإشعار.

الحقلالوصف
bodystring ≤ 2000إلزامي
نص الإشعار.
titlestring ≤ 255اختياري
العنوان.
urlstringاختياري
يُفتح عند الضغط: رابط https:// أو رابط داخلي مثل neonstore://app/1234.
image_urlhttps URLاختياري
صورة كبيرة (أندرويد؛ iOS يحتاج إضافة في التطبيق).
dataobject ≤ 30اختياري
بيانات إضافية تصل كنصوص. المفاتيح التي تبدأ بـ kd_ محجوزة.
audienceobjectاختياري
{"type":"all"} (الافتراضي) · users + external_user_ids · devices + installation_ids · tags + tags — حتى 1000 معرّف.
platforms["ios","android","web"]اختياري
حدد منصة أو أكثر، أو اتركه للجميع.
scheduled_atISO 8601اختياري
جدولة خلال 30 يوماً.
ttl_secondsintاختياري
مدة الاحتفاظ إذا كان الجهاز مغلقاً (الافتراضي 3 أيام، الحد 28 يوماً).
priorityhigh | normalاختياري
الافتراضي high.
collapse_idstring ≤ 64اختياري
إشعار جديد بنفس المعرّف يستبدل القديم على الجهاز.

الترويسات

الحقلالوصف
Idempotency-Keystring ≤ 128اختياري
موصى به بشدة. لماذا؟
يتطلب اشتراكاً فعّالاً للتاجر، وإلا 402 subscription_inactive. وبدون اشتراك لا يعمل أي شيء: لا إعدادات (/sdk/config) ولا تسجيل أجهزة ولا تعديل إعدادات التطبيق من خادمك.
Request
curl -X POST https://push.kd-app.com/api/v1/messages \
  -H "Authorization: Bearer kdp_live_…" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: app-created-1234" \
  -d '{
    "title": "تطبيق جديد",
    "body": "تمت إضافة واتساب الذهبي للمتجر",
    "url": "neonstore://app/1234",
    "image_url": "https://cdn.example.com/icons/1234.png",
    "data": { "app_id": "1234" },
    "audience": { "type": "all" }
  }'
202 Accepted
{
  "message": {
    "id": "01JB2QA4N7C2X9V5B8M3K6P1RD",
    "status": "queued",
    "source": "api",
    "title": "تطبيق جديد",
    "body": "تمت إضافة واتساب الذهبي للمتجر",
    "image_url": "https://cdn.example.com/icons/1234.png",
    "url": "neonstore://app/1234",
    "data": {
      "app_id": "1234"
    },
    "audience": {
      "type": "all"
    },
    "platforms": null,
    "scheduled_at": null,
    "counts": {
      "targeted": 0,
      "sent": 0,
      "failed": 0,
      "invalid": 0,
      "opened": 0
    },
    "error": null,
    "failures": [],
    "created_at": "2026-10-03T12:00:00+00:00",
    "started_at": null,
    "finished_at": null
  }
}
402 Payment Required
{
  "error": {
    "code": "subscription_inactive",
    "message": "No active KD Push subscription for this account."
  }
}
Platform backend

حالة إشعار

GET/messages/{id}Server Key — خادمك

الحالة والعدّادات. راجع كائن الإشعار لمعنى كل حقل.

Request
curl https://push.kd-app.com/api/v1/messages/01JB2QA4N7C2X9V5B8M3K6P1RD \
  -H "Authorization: Bearer kdp_live_…"
200 OK
{
  "message": {
    "id": "01JB2QA4N7C2X9V5B8M3K6P1RD",
    "status": "sent",
    "source": "api",
    "title": "تطبيق جديد",
    "body": "تمت إضافة واتساب الذهبي للمتجر",
    "image_url": "https://cdn.example.com/icons/1234.png",
    "url": "neonstore://app/1234",
    "data": {
      "app_id": "1234"
    },
    "audience": {
      "type": "all"
    },
    "platforms": null,
    "scheduled_at": null,
    "counts": {
      "targeted": 1240,
      "sent": 1227,
      "failed": 3,
      "invalid": 10,
      "opened": 214
    },
    "error": null,
    "failures": [],
    "created_at": "2026-10-03T12:00:00+00:00",
    "started_at": "2026-10-03T12:00:01+00:00",
    "finished_at": "2026-10-03T12:00:04+00:00"
  }
}
Platform backend

سجل الإشعارات

GET/messagesServer Key — خادمك

سجل تطبيقك، الأحدث أولاً.

الحقلالوصف
statusstringاختياري
scheduled · queued · sending · sent · failed · canceled
per_page1–100اختياري
الافتراضي 20.
Request
curl "https://push.kd-app.com/api/v1/messages?status=sent&per_page=20" \
  -H "Authorization: Bearer kdp_live_…"
200 OK
{
  "data": [
    {
      "id": "01JB2QA4N7C2X9V5B8M3K6P1RD",
      "status": "sent",
      "source": "api",
      "title": "تطبيق جديد",
      "body": "تمت إضافة واتساب الذهبي للمتجر",
      "image_url": "https://cdn.example.com/icons/1234.png",
      "url": "neonstore://app/1234",
      "data": {
        "app_id": "1234"
      },
      "audience": {
        "type": "all"
      },
      "platforms": null,
      "scheduled_at": null,
      "counts": {
        "targeted": 0,
        "sent": 0,
        "failed": 0,
        "invalid": 0,
        "opened": 0
      },
      "error": null,
      "failures": [],
      "created_at": "2026-10-03T12:00:00+00:00",
      "started_at": null,
      "finished_at": null
    }
  ],
  "meta": {
    "current_page": 1,
    "per_page": 20,
    "total": 1,
    "last_page": 1
  }
}
Platform backend

إلغاء إشعار

POST/messages/{id}/cancelServer Key — خادمك

يلغي إشعاراً مجدولاً أو في الطابور أو قيد الإرسال. الدفعات التي لم تُرسل بعد تتوقف فوراً.

Request
curl -X POST https://push.kd-app.com/api/v1/messages/01JB2QA4N7C2X9V5B8M3K6P1RD/cancel \
  -H "Authorization: Bearer kdp_live_…"
200 OK
{
  "message": {
    "id": "01JB2QA4N7C2X9V5B8M3K6P1RD",
    "status": "canceled",
    "source": "api",
    "title": "تطبيق جديد",
    "body": "تمت إضافة واتساب الذهبي للمتجر",
    "image_url": "https://cdn.example.com/icons/1234.png",
    "url": "neonstore://app/1234",
    "data": {
      "app_id": "1234"
    },
    "audience": {
      "type": "all"
    },
    "platforms": null,
    "scheduled_at": null,
    "counts": {
      "targeted": 0,
      "sent": 0,
      "failed": 0,
      "invalid": 0,
      "opened": 0
    },
    "error": null,
    "failures": [],
    "created_at": "2026-10-03T12:00:00+00:00",
    "started_at": null,
    "finished_at": null
  }
}
409 Conflict
{
  "error": {
    "code": "not_cancelable",
    "message": "Only scheduled, queued or sending messages can be canceled."
  }
}
Platform backend

التطبيق والاشتراك

GET/appServer Key — خادمك

تحقق من المفتاح ومن إمكانية الإرسال قبل تفعيل الإشعارات التلقائية في منصتك: sending.can_send يجمع حالة الاشتراك وحصة الشهر، وsending.reason يذكر السبب بنفس رموز POST /messages (subscription_inactive · quota_exceeded). plan وusage لعرض وضع التاجر، وportal روابط يصلح منها اشتراكه.

Request
curl https://push.kd-app.com/api/v1/app \
  -H "Authorization: Bearer kdp_live_…"
200 OK
{
  "app": {
    "id": "01JB2Q6V8K4Y3N5M7R9T1W0XZC",
    "name": "My Store",
    "platform": "neon",
    "status": "active",
    "ios_bundle_id": "com.store.app",
    "android_package": "com.store.app"
  },
  "subscription": {
    "status": "active",
    "expires_at": "2026-11-02T12:00:00+00:00",
    "grace_until": null,
    "days_left": 19,
    "can_send": true
  },
  "plan": {
    "name": "Pro",
    "period": "monthly",
    "monthly_messages": 100000,
    "max_devices": null,
    "max_apps": 3
  },
  "usage": {
    "deliveries_this_month": 18240,
    "remaining_this_month": 81760,
    "active_devices": 1240
  },
  "sending": {
    "can_send": true,
    "reason": null
  },
  "portal": {
    "plans": "https://push.kd-app.com/portal/plans",
    "subscription": "https://push.kd-app.com/portal/subscription"
  },
  "devices": {
    "active": 1240,
    "ios": 800,
    "android": 440,
    "web": 0
  },
  "web": {
    "enabled": false,
    "origins": [],
    "vapid_public_key": null,
    "subscribers": 0,
    "icon_url": null,
    "default_url": null,
    "prompt": {
      "enabled": true,
      "delay_seconds": 6,
      "title": null,
      "text": null
    }
  }
}
Platform backend

حزمة أندرويد

PUT/app/androidServer Key — خادمك

لمنصة تتيح لتاجرها اختيار حزمة تطبيق أندرويد: أرسل الحزمة التي بُني بها التطبيق عند تغيّرها، فتظهر في بوابة التاجر ويُتحقَّق منها ملف Firebase الذي يرفعه لاحقاً.

الحقلالوصف
packagestringإلزامي
مثل com.store.app.
إذا اختار التاجر مشروع Firebase خاصاً به لهذا التطبيق وكان مسجّلاً لحزمة أخرى، يُرفض الطلب بـ 409 package_mismatch ويذكر firebase_package الحزمة المسجّلة — حتى لا تتوقف إشعارات أندرويد بصمت.
Request
curl -X PUT https://push.kd-app.com/api/v1/app/android \
  -H "Authorization: Bearer kdp_live_…" \
  -H "Content-Type: application/json" \
  -d '{"package":"com.store.app"}'
200 OK
{
  "app": {
    "id": "01JB2Q6V8K4Y3N5M7R9T1W0XZC",
    "android_package": "com.store.app"
  }
}
409 Conflict
{
  "error": {
    "code": "package_mismatch",
    "message": "The app's Firebase project is registered for com.store.app; upload one for com.other.app first.",
    "firebase_package": "com.store.app"
  }
}
Platform backend

إحصائيات الأجهزة

GET/devices/statsServer Key — خادمك

عدد الأجهزة النشطة لكل منصة.

Request
curl https://push.kd-app.com/api/v1/devices/stats \
  -H "Authorization: Bearer kdp_live_…"
200 OK
{
  "devices": {
    "active": 1240,
    "ios": 800,
    "android": 440,
    "web": 0
  }
}
Connect

ربط مع KD Push

اربط متجر التاجر بتطبيق KD Push بنقرة واحدة، دون أن ينسخ أحد مفتاحاً: التاجر يوافق في صفحة KD Push، ومنصتك تستلم المفاتيح مباشرة من خادم إلى خادم (OAuth 2 بأسلوب authorization code مع PKCE).

  1. نسجّل منصتك ونعطيك client_id وclient_secret، مع عناوين العودة الدقيقة المسموحة.
  2. أرسل التاجر إلى /portal/connect/authorize بالمعاملات أدناه.
  3. يسجّل دخوله أو ينشئ حساباً (معبّأ مسبقاً مما أرسلته)، يختار تطبيقاً أو ينشئه، ويوافق.
  4. نعيده إلى redirect_uri?code=…&state=… — أو error=access_denied إن رفض.
  5. خادمك يستبدل الرمز بالمفاتيح عبر POST /connect/token.

معاملات صفحة الموافقة

الحقلالوصف
response_typecodeإلزامي
دائماً code.
client_idstringإلزامي
معرّف منصتك.
redirect_urihttps URLإلزامي
يطابق حرفياً عنواناً مسجّلاً لمنصتك.
statestring ≤ 255إلزامي
قيمة عشوائية تحفظها وتتحقق منها عند العودة.
code_challengebase64urlإلزامي
BASE64URL(SHA256(code_verifier))؛ احتفظ بـ code_verifier (43–128 حرفاً) في خادمك.
code_challenge_methodS256إلزامي
دائماً S256.
app_namestring ≤ 120اختياري
اسم التطبيق الجديد إن لم يكن لدى التاجر تطبيق.
ios_bundle_idstringاختياري
يُقدَّم التطبيق المطابق أولاً، ويُسجَّل المعرّف لدى Apple تلقائياً.
android_packagestringاختياري
الحزمة التي يُبنى بها تطبيق أندرويد؛ يتعلّمها التطبيق الموجود إن لم تكن لديه.
external_refstring ≤ 191اختياري
معرّف المتجر عندك؛ إعادة الربط لنفس المتجر تستبدل مفتاحه السابق.
external_labelstring ≤ 255اختياري
ما يظهر للتاجر (مثل نطاق المتجر).
hint_name · hint_email · hint_company · hint_phonestringاختياري
تعبئة نموذج إنشاء الحساب مسبقاً.
تحقّق من state قبل أي شيء. الرمز صالح 10 دقائق ولمرة واحدة؛ تقديمه مرتين يُلغي ما نتج عنه. لا يصل code_verifier ولا client_secret إلى المتصفح أبداً.
Request
// 1. Send the merchant to KD Push (keep $verifier and $state in their session)
$verifier = Str::random(64);
$state = Str::random(40);
session(['kdpush' => compact('verifier', 'state')]);

return redirect('https://push.kd-app.com/portal/connect/authorize?' . http_build_query([
    'response_type' => 'code',
    'client_id' => config('services.kdpush.connect_client_id'),
    'redirect_uri' => 'https://your-platform.example/kdpush/callback',
    'state' => $state,
    'code_challenge' => rtrim(strtr(base64_encode(hash('sha256', $verifier, true)), '+/', '-_'), '='),
    'code_challenge_method' => 'S256',
    'app_name' => $store->name,           // optional: prefills a new app
    'ios_bundle_id' => $store->bundleId,  // optional
    'android_package' => $store->androidPackage, // optional
    'external_ref' => $store->id,         // optional: your id for the store
    'hint_email' => $owner->email,        // optional: prefills sign-up
], '', '&', PHP_QUERY_RFC3986));
Connect

استبدال الرمز بالمفاتيح

POST/connect/tokenclient_id + client_secret
الحقلالوصف
grant_typeauthorization_codeإلزامي
ثابت.
client_id · client_secretstringإلزامي
بيانات منصتك.
codestringإلزامي
من عنوان العودة.
redirect_uristringإلزامي
نفس العنوان المستخدم في الطلب.
code_verifierstringإلزامي
القيمة التي احتفظت بها.

تصلك app_key (للتطبيق)، وserver_key جديد خاص بهذا الربط (يظهر مرة واحدة)، وidentity_secret، ووضع اشتراك التاجر في account بنفس شكل GET /app.

مفتاح الخادم هو الربط نفسه: إلغاؤه من أي طرف يُنهي الربط، وإعادة ربط نفس المتجر تستبدله بدل أن تتراكم المفاتيح.
Request
curl -X POST https://push.kd-app.com/api/v1/connect/token \
  -H "Content-Type: application/json" \
  -d '{
    "grant_type": "authorization_code",
    "client_id": "kdp_cli_…",
    "client_secret": "kdp_cs_…",
    "code": "kdp_code_…",
    "redirect_uri": "https://your-platform.example/kdpush/callback",
    "code_verifier": "…"
  }'
200 OK
{
  "connection": {
    "id": "01JB2QX5V8K4Y3N5M7R9T1W0XZ",
    "connected_at": "2026-10-14T12:00:00+00:00"
  },
  "app": {
    "id": "01JB2Q6V8K4Y3N5M7R9T1W0XZC",
    "name": "My Store",
    "platform": "neon",
    "ios_bundle_id": "com.store.app",
    "android_package": null
  },
  "merchant": {
    "name": "Store owner",
    "email": "[email protected]"
  },
  "app_key": "kdp_app_…",
  "server_key": "kdp_live_…",
  "identity_secret": "…",
  "api_base": "https://push.kd-app.com/api/v1",
  "account": {
    "subscription": {
      "status": "active",
      "expires_at": "2026-11-02T12:00:00+00:00",
      "grace_until": null,
      "days_left": 19,
      "can_send": true
    },
    "plan": {
      "name": "Pro",
      "period": "monthly",
      "monthly_messages": 100000,
      "max_devices": null,
      "max_apps": 3
    },
    "usage": {
      "deliveries_this_month": 18240,
      "remaining_this_month": 81760,
      "active_devices": 1240
    },
    "sending": {
      "can_send": true,
      "reason": null
    },
    "portal": {
      "plans": "https://push.kd-app.com/portal/plans",
      "subscription": "https://push.kd-app.com/portal/subscription"
    }
  }
}
400 Bad Request
{
  "error": {
    "code": "invalid_grant",
    "message": "Authorization code already used."
  }
}
Connect

فصل الربط

POST/connect/revokeServer Key — خادمك

يتوقف مفتاح الخادم المرسَل فوراً؛ التطبيق وأجهزته وسجله تبقى للتاجر. التاجر يستطيع الفصل أيضاً من لوحته، وعندها يجيب مفتاحك بـ 401 invalid_server_key — اعتبر ذلك فصلاً.

Request
curl -X POST https://push.kd-app.com/api/v1/connect/revoke \
  -H "Authorization: Bearer kdp_live_…"
200 OK
{
  "revoked": true
}
Reference

كائن الإشعار

scheduled→queued→sending→sent·failedcanceled
الحقلالوصف
idULIDاختياري
معرّف الإشعار.
statusstringاختياري
sent إذا وصل لجهاز واحد على الأقل، failed إذا لم يصل لأي جهاز (السبب في error).
sourceapi | portal | adminاختياري
مصدر الإرسال.
counts.targetedintاختياري
الأجهزة المستهدفة.
counts.sentintاختياري
قبلها Apple/Google للتوصيل.
counts.invalidintاختياري
أجهزة ميتة (حُذف التطبيق مثلاً) — تُستبعد تلقائياً بعدها.
counts.failedintاختياري
فشل لأسباب أخرى بعد إعادة المحاولة.
counts.openedintاختياري
مرات الفتح المبلّغ عنها.
failures[{provider, code, http_status, count}]اختياري
لماذا لم يصل لبعض الأجهزة، الأكثر أولاً: رمز Apple أو Firebase كما هو (مثل TopicDisallowed) وحالة HTTP وعدد الأجهزة.
created_at · started_at · finished_at · scheduled_atISO 8601اختياري
أوقات دورة الحياة.
Guide

التحقق من هوية المستخدم

لربط جهاز بمستخدم (لإشعار شخصي مثل "اشتراكك ينتهي")، يحتاج التطبيق توقيعاً يحسبه خادمك بعد تسجيل الدخول — وإلا يستطيع أي شخص تسجيل جهازه باسم مستخدم آخر واستقبال إشعاراته.

Formula
user_signature = hex( HMAC-SHA256( external_user_id , identity_secret ) )
على خادمك
// After the user logs in — on your backend, never in the app
$signature = hash_hmac('sha256', (string) $user->id, config('services.kdpush.identity_secret'));

return ['user_id' => $user->id, 'push_signature' => $signature];
السر من البوابة: التطبيق ← المفاتيح ← سر التحقق. لا تضعه داخل التطبيق أبداً.
Guide

ما يستقبله التطبيق

iOS — APNs
{
  "aps": {
    "alert": {
      "title": "تطبيق جديد",
      "body": "تمت إضافة واتساب الذهبي للمتجر"
    },
    "sound": "default",
    "mutable-content": 1
  },
  "kd_message_id": "01JB2QA4N7C2X9V5B8M3K6P1RD",
  "kd_url": "neonstore://app/1234",
  "kd_image": "https://cdn.example.com/icons/1234.png",
  "app_id": "1234"
}
Android — FCM
{
  "notification": {
    "title": "تطبيق جديد",
    "body": "تمت إضافة واتساب الذهبي للمتجر",
    "image": "https://cdn.example.com/icons/1234.png"
  },
  "data": {
    "kd_message_id": "01JB2QA4N7C2X9V5B8M3K6P1RD",
    "kd_url": "neonstore://app/1234",
    "app_id": "1234"
  }
}

افتح kd_url عند الضغط، وأرسل kd_message_id إلى /sdk/events. قيم data تصل كنصوص.

Flutter
FirebaseMessaging.onMessageOpenedApp.listen((message) {
  final url = message.data['kd_url'];
  final id = message.data['kd_message_id'];
  if (url != null) openDeepLink(url);   // e.g. neonstore://app/1234
  if (id != null) reportOpened(id);     // POST /sdk/events
});
Guide

خطوات ربط منصة

  1. 1التاجر ينشئ تطبيقه في البوابة ويضع App Key و Server Key في لوحة منصتك.
  2. 2منصتك تبني التطبيق وبداخله App Key — والتطبيق يطلب /sdk/config ثم يسجّل الجهاز.
  3. 3بعد تسجيل الدخول يعطي خادمك التطبيق user_signature لمعرّف المستخدم.
  4. 4منصتك ترسل الإشعارات التلقائية (تطبيق جديد، تحديث) مع Idempotency-Key لكل حدث.
  5. 5عند ربط كتالوج لأول مرة لا ترسل إشعاراً لكل تطبيق — اجمعها في إشعار واحد.
  6. 6تحقق من subscription.can_send عبر /app قبل تفعيل الإشعارات التلقائية.
Tools

Postman

مجموعة Postman جاهزةv2.1كل الطلبات بأمثلة وردود، والمصادقة جاهزة — ضع مفاتيحك وجرّب خلال ثوانٍ.
  1. حمّل المجموعة واستوردها في Postman (Import ← اختر الملف).
  2. من متغيرات المجموعة ضع app_key و server_key، وتوكن جهاز تجريبي في device_token.
  3. شغّل Register device ثم Send a notification — معرّف الإشعار يُحفظ تلقائياً في message_id للطلبات التالية.
  4. اختياري: ضع external_user_id و identity_secret والمجموعة تحسب user_signature بنفسها.