نظرة عامة على API
توفّر Joryio REST API وصولًا برمجيًا إلى جميع ميزات المنصة.
عنوان الأساس
https://api-eu1.joryio.com/api
تُخدَم جميع مساحات العمل حاليًا من منطقة واحدة؛ وستوثق نقاط النهاية الخاصة بالمناطق عند إطلاق مناطق إضافية.
المصادقة
تتطلب جميع طلبات API المصادقة عبر مفتاح API. يرتبط المفتاح بمساحة عمل واحدة ويحمل مجموعة صلاحيات صريحة ويمكن تقييده بقائمة IPs. راجع مفاتيح API لكتالوج النطاقات الكامل وسلوك قائمة سماح IP.
GET /api/users/by-user-id/user_123
Host: api-eu1.joryio.com
Authorization: Bearer jry_live_your_api_key_here
Content-Type: application/json
مرّر المفتاح كـ bearer token في رأس Authorization مع كل طلب. تبدأ المفاتيح دائمًا بـ jry_live_ للإنتاج أو jry_test_ للاختبار. ويمكن تسجيل بادئة المفتاح المرئية، مثل jry_live_98f31a72، بأمان؛ أما الباقي فهو سري.
الحصول على مفتاح API
- سجّل الدخول إلى لوحة Joryio.
- انتقل إلى Settings ← API Keys.
- انقر Create API Key، واختر النطاقات التي يحتاجها التكامل، ثم انسخ القيمة التي تظهر مرة واحدة.
لا ترفع مفاتيح API إلى التحكم بالإصدارات ولا تكشفها في كود جهة العميل. تظهر القيمة الكاملة مرة واحدة فقط بعد الإنشاء؛ خزّنها فورًا في مدير الأسرار لديك.
مجموعة Postman
أسرع طريقة لاستكشاف API: استورد المجموعة الرسمية إلى Postman. تتضمن كل نقطة نهاية عامة ونص مثالًا، ومضبوطة مسبقًا على متغير {{baseUrl}} ومصادقة bearer token.
- نزّل المجموعة.
- في Postman: Import ثم أسقط الملف.
- اضبط متغيرات المجموعة:
baseUrl=https://api-eu1.joryio.com/apiوtoken= مفتاح API،jry_live_....
صيغة الطلب
تستخدم جميع الطلبات والاستجابات JSON:
POST /users/track
Content-Type: application/json
{
"attributes": {
"externalId": "user_123",
"email": "[email protected]",
"attributes": {
"plan": "premium"
}
}
}
صيغة الاستجابة
تعيد نقاط النهاية JSON للمورد مباشرة؛ لا يوجد غلاف { "success": true, "data": ... }.
استجابة نجاح
مثلًا، يعيد GET /users/by-user-id/user_123 كائن المستخدم نفسه:
{
"id": "665f1e2a9b3c4d5e6f7a8b9c",
"userId": "665f1e2a9b3c4d5e6f7a8b9c",
"externalId": "user_123",
"phone": "+14155550123",
"attributes": { "plan": "premium" },
"createdAt": "2026-01-15T10:30:00.000Z",
"updatedAt": "2026-01-15T10:30:00.000Z"
}
id / userId هو معرّف Joryio الداخلي؛ أما المعرّف الذي قدمته فيظهر كـ externalId.
استجابة خطأ
تشترك كل الأخطاء في شكل واحد ينتجه مرشح استثناءات عام:
{
"statusCode": 400,
"message": "Cannot create user without a valid identifier (userId, externalId, or email)",
"timestamp": "2026-01-15T10:30:00.000Z",
"path": "/users"
}
تحمل أخطاء تحقق الطلب، 400، أيضًا مصفوفة errors برسالة لكل حقل فاشل:
{
"statusCode": 400,
"message": "Bad Request Exception",
"timestamp": "2026-01-15T10:30:00.000Z",
"path": "/api/users/track",
"errors": [
"eventName must be shorter than or equal to 500 characters"
]
}
أخطاء جسم الطلب
يُرفض الجسم الذي لا يستطيع الخادم قراءته قبل وصوله إلى نقطة النهاية، مع message يسمّي المشكلة. لا يُعاد الجسم الذي أرسلته في الاستجابة أبدًا.
| الحالة | message | السبب |
|---|---|---|
400 | Request body is not valid JSON. | يحتوي JSON على خطأ في الصياغة. |
400 | Request body must be a JSON object or array - a bare string, number, boolean or null is not accepted. | المستوى الأعلى ليس كائنًا {...} أو مصفوفة [...]. |
413 | Request body is too large - the limit is 1 MB. | حجم الجسم أكبر من 1 MB. |
415 | Request body charset is not supported - send UTF-8. | يحدد Content-Type مجموعة محارف غير UTF-8. |
رموز حالة HTTP
| الرمز | المعنى | الوصف |
|---|---|---|
200 | OK | نجح الطلب |
201 | Created | أُنشئ المورد بنجاح |
204 | No Content | نجح الحذف، نص استجابة فارغ |
400 | Bad Request | معاملات طلب غير صالحة |
401 | Unauthorized | مفتاح API مفقود أو غير صالح |
403 | Forbidden | لا يملك مفتاح API الصلاحيات المطلوبة |
404 | Not Found | المورد غير موجود |
409 | Conflict | المورد موجود مسبقًا |
413 | Payload Too Large | جسم الطلب أكبر من 1 MB |
415 | Unsupported Media Type | مجموعة محارف أو ترميز جسم غير مدعوم |
429 | Too Many Requests | تجاوز حد المعدل |
500 | Internal Server Error | حدث خطأ في الخادم |
503 | Service Unavailable | الخدمة غير متاحة مؤقتًا |
تحديد المعدل
تفرض نقاط نهاية API المصادَق عليها سقفًا لكل مؤسسة. هذه سقف لمنع إساءة الاستخدام، وليست ميزانية إنتاجية: فهي أعلى بكثير من الاستخدام الطبيعي، والسعة يتكفل بها أسطول العاملين لدينا وليس رفض الطلبات عند الحافة.
| السطح | الحد | حسب |
|---|---|---|
| طلبات API | 1,200/دقيقة | مؤسستك |
استيعاب جهات الاتصال والأحداث (POST /users/track) | 6,000/دقيقة | مؤسستك |
| تسجيل الدخول وإعادة تعيين كلمة المرور | 5-10/دقيقة | عنوان IP |
تُحتسب الحدود المصادَق عليها حسب مؤسستك لا حسب عنوان IP: بذلك يتشارك أسطول من الخوادم ميزانية واحدة بدل أن ينال كل خادم ميزانيته، ولا يؤثر عميلان خلف الشبكة نفسها أحدهما على الآخر.
تظهر حدود حسابك الحالية في الإعدادات -> الاستخدام، ويمكن رفعها. إذا اقترب استيراد ضخم من أحدها فراسل [email protected].
مستقبِلات webhook الواردة - أحداث التسليم والردود واستدعاءات منصات التجارة - بلا حد عمدًا. الـ webhook المرفوض يعني فقدان بيانات، لذا تُحمى هذه النقاط بالتحقق من التوقيع بدل العدّاد.
X-RateLimit-Limit: 1200
X-RateLimit-Remaining: 1188
X-RateLimit-Reset: 60
HTTP/1.1 429 Too Many Requests
Retry-After: 42
{
"statusCode": 429,
"message": "Too Many Requests",
"timestamp": "2026-01-15T10:30:00.000Z",
"path": "/auth/login"
}
التعامل مع حدود المعدل
قد تُدخل الحدود أو تُشدد بمرور الوقت. عامل 429 دائمًا كقابل لإعادة المحاولة، واحترم Retry-After ونفذ تراجعًا أسيًا:
async function makeRequestWithRetry(url, options, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
const response = await fetch(url, options);
if (response.status === 429) {
const retryAfter = response.headers.get('Retry-After') || Math.pow(2, i);
await sleep(retryAfter * 1000);
continue;
}
return response;
}
}
ترقيم الصفحات
تستخدم نقاط نهاية السرد معاملات الاستعلام limit / offset:
GET /users?limit=50&offset=100
المعاملات:
limit: النتائج في الصفحة؛ تختلف القيم الافتراضية والقصوى حسب نقطة النهاية. المستخدمون: الافتراضي 50 والحد 200؛ الشرائح: الافتراضي والحد 100؛ استعلام الأحداث: الافتراضي 100 والحد 1000.offset: عدد العناصر التي يجب تخطيها؛ الافتراضي 0.
الاستجابة:
تغلّف استجابات السرد المرقمة الصفحة في مصفوفة data وكائن pagination:
{
"data": [...],
"pagination": {
"total": 1234,
"limit": 50,
"offset": 100,
"hasMore": true
}
}
تتضمن بعض النقاط حقول ترقيم إضافية، مثل page / totalPages، وتعید بعض القوائم الأصغر مصفوفة JSON مباشرة. توثق صفحة كل نقطة شكلها الدقيق.
الفلترة
لا توجد صيغة استعلام عامة مثل filter[field] أو sort. تعرض نقاط السرد معاملات فلترة خاصة بها، مثل:
GET /events/query?eventName=Order+Completed&startDate=2026-01-01&endDate=2026-01-31
GET /segments?q=vip&status=active&tags=onboarding
GET /users/search?query=jane
تعاد النتائج بترتيب ثابت: الأحدث أولًا.
عدم التكرار
لا يوجد رأس طلب عام Idempotency-Key. تُوفّر سلامة إعادة المحاولة لكل نقطة نهاية:
POST /users/trackعملية upsert تعتمدexternalIdلديك - إعادة الطلب نفسه تحدّث الملف نفسه بدل إنشاء تكرار.- يقبل كل عنصر في
eventsقيمةclientEventIdاختيارية؛ تُستخدم كمعرّف للحدث المخزن، لذلك تُزال تكرارات الطلب المعاد بالقيمة نفسها منclientEventId. POST /campaigns/transactional/sendيقبلidempotencyKeyاختياريًا في النص؛ إعادة المحاولة بالمفتاح نفسه لا ترسل مرتين.
الطوابع الزمنية
تستخدم جميع الطوابع ISO 8601 مع المنطقة الزمنية UTC:
{
"createdAt": "2024-01-15T10:30:00.000Z",
"updatedAt": "2024-01-15T14:45:30.000Z"
}
نقاط API
Users API
| الطريقة | نقطة النهاية | الوصف |
|---|---|---|
| POST | /users/track | إنشاء الأشخاص أو تحديثهم وتسجيل أحداثهم ومشترياتهم وأجهزة الإشعارات لديهم - كل حقل يقبل كائناً واحداً أو مصفوفة (حتى 1000 لـ attributes في الطلب) |
| GET | /users | سرد المستخدمين، limit / offset |
| GET | /users/search | البحث عن المستخدمين بالبريد أو الاسم أو الهاتف أو المعرّف |
| GET | /users/:userId | جلب المستخدم بالمعرّف الداخلي لـ Joryio |
| GET | /users/by-user-id/:userId | جلب المستخدم بـ userId الخاص بك |
| PUT | /users/:userId | تحديث المستخدم بالمعرّف الداخلي |
| PUT | /users/by-user-id/:userId | تحديث المستخدم بـ userId الخاص بك |
| DELETE | /users/:userId | حذف المستخدم، يعيد 204 |
Events API
| الطريقة | نقطة النهاية | الوصف |
|---|---|---|
| POST | /users/track | تتبع حدث واحد أو عدة أحداث - يقبل events كائناً واحداً أو مصفوفة (حتى 500 في الطلب) |
| GET | /events/query | الاستعلام عن الأحداث بفلاتر |
| POST | /events/aggregate | تجميع مقاييس الأحداث بمرور الوقت |
Campaigns API
| الطريقة | نقطة النهاية | الوصف |
|---|---|---|
| POST | /campaigns | إنشاء حملة |
| GET | /campaigns/:id | جلب حملة |
| PUT | /campaigns/:id | تحديث حملة |
| DELETE | /campaigns/:id | حذف حملة |
| POST | /campaigns/:id/send | إرسال حملة |
| GET | /campaigns/:id/stats | جلب إحصاءات حملة |
Segments API
| الطريقة | نقطة النهاية | الوصف |
|---|---|---|
| POST | /segments | إنشاء شريحة |
| GET | /segments | سرد الشرائح |
| GET | /segments/:id | جلب شريحة |
| PUT | /segments/:id | تحديث شريحة |
| POST | /segments/:id/archive | أرشفة شريحة؛ لا يمكن حذف الشرائح حذفًا نهائيًا |
| GET | /segments/:id/users | جلب مستخدمي شريحة |
| GET | /segments/:id/size | جلب حجم الشريحة |
Apps API
| الطريقة | نقطة النهاية | الوصف |
|---|---|---|
| POST | /apps | إنشاء تطبيق |
| GET | /apps/:id | جلب تطبيق |
| PUT | /apps/:id | تحديث تطبيق |
| DELETE | /apps/:id | حذف تطبيق |
| POST | /apps/:id/regenerate-key | إعادة إنشاء مفتاح SDK |
| GET | /apps/:id/stats | جلب إحصاءات التطبيق |
SDKs
لتكامل أسهل، استخدم SDKs الرسمية:
- Web SDK: npm install @joryio/web-sdk
- iOS SDK: Swift Package / CocoaPods
- Android SDK: Gradle
- React Native SDK: npm install @joryio/react-native-sdk
أمثلة
إنشاء مستخدم
curl -X POST https://api-eu1.joryio.com/api/users/track \
-H "Authorization: Bearer jry_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"attributes": {
"externalId": "user_123",
"email": "[email protected]",
"attributes": {
"firstName": "John",
"lastName": "Doe",
"plan": "premium"
}
}
}'
تتبع حدث
curl -X POST https://api-eu1.joryio.com/api/users/track \
-H "Authorization: Bearer jry_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"events": {
"externalId": "user_123",
"name": "Order Completed",
"properties": { "orderId": "order_456", "total": 99.99 }
}
}'
إنشاء شريحة
curl -X POST https://api-eu1.joryio.com/api/segments \
-H "Authorization: Bearer jry_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"name": "Premium Users",
"description": "Users on premium plan",
"filterGroups": [{
"filters": [{
"type": "attribute",
"field": "plan",
"operator": "equals",
"value": "premium"
}],
"operator": "AND"
}],
"groupOperator": "AND"
}'
إرسال حملة
لا تحتاج نقطة الإرسال إلى نص طلب: إذ يحدد إعداد الحملة المحفوظ ما يُرسل.
curl -X POST https://api-eu1.joryio.com/api/campaigns/:id/send \
-H "Authorization: Bearer jry_live_your_api_key"
الأخطاء
لا توجد مفردات منفصلة لرموز أخطاء قابلة للقراءة آليًا. استخدم رمز HTTP مع حقل message في نص الخطأ القياسي، راجع صيغة الاستجابة:
{
"statusCode": 404,
"message": "Segment with ID 3f9d2c1e-7a54-4b2e-9c1d-8e6f5a4b3c2d not found",
"timestamp": "2026-01-15T10:30:00.000Z",
"path": "/segments/3f9d2c1e-7a54-4b2e-9c1d-8e6f5a4b3c2d"
}
الاختبار
يمكن إنشاء المفاتيح بتسمية test، مثل jry_test_...، لتمييز مفاتيح التكامل عن مفاتيح الإنتاج سريعًا؛ ولا تغير التسمية ما يمكن للمفتاح فعله. للتجارب الآمنة، أنشئ مساحة عمل منفصلة للاختبار: تعزل مساحات العمل الملفات والأحداث والحملات تمامًا، لذلك لا يلامس ما تجربه بيانات أو مستلمين في الإنتاج. تتوفر إرسالات اختبار القناة، مثل Email وSMS، داخل محررات الحملات.
الدعم
هل تحتاج إلى مساعدة؟