Campaigns API
أنشئ الحملات وأطلقها وراقبها برمجياً.
كل نقاط النهاية في هذه الصفحة نسبية إلى عنوان الأساس: https://api-eu1.joryio.com. راجع نظرة عامة على API.
المصادقة
تتطلب جميع الطلبات مصادقة بمفتاح API:
Authorization: Bearer jry_live_your_api_key_here
Content-Type: application/json
يجب أن يحمل مفتاحك النطاق المطلوب لكل نقطة نهاية:
| النطاق | نقاط النهاية |
|---|---|
campaigns:read | العرض والقائمة والإحصاءات والمستلمون والإصدارات والسجل |
campaigns:write | الإنشاء والتحديث والتكرار والأرشفة والوسوم واسترجاع الإصدارات |
campaigns:send | الإرسال والإيقاف والاستئناف والإلغاء وإعادة المحاولة وإرسال الاختبارات والإرسال المعاملي |
campaigns:delete | الحذف والحذف الجماعي |
راجع مفاتيح API لإدارة النطاقات.
إنشاء حملة
نقطة النهاية
POST /campaigns
نص الطلب
| الحقل | النوع | مطلوب | الوصف |
|---|---|---|---|
name | string | نعم | اسم الحملة، بحد أقصى 255 حرفاً |
description | string | لا | الوصف، بحد أقصى 1000 حرف |
channel | string | نعم | email أو sms أو viber أو push أو webhook أو whatsapp أو in_app أو ai_optimized |
variants | array | نعم* | متغيرات الرسالة؛ مطلوبة لكل قناة عدا in_app |
targeting | object | لا | الجمهور: userIds وfilterGroups وexcludeFilterGroups وfilterOperator وsubscriptionPreference |
sendType | string | لا | immediate أو scheduled أو triggered أو intelligent أو recurring أو ai_optimized |
scheduledAt | string | لا | تاريخ ISO 8601 لـ sendType: "scheduled" |
scheduledTimezone | string | لا | المنطقة الزمنية IANA التي يفسر بها الوقت المجدول |
triggerConfig | object | لا | قاعدة تشغيل لـ sendType: "triggered"، وتشمل type وeventName وconditions وreEntry وcooldownHours |
recurringSchedule | object | لا | لـ sendType: "recurring": frequency، أي daily/weekly/monthly/custom، وcron وdayOfWeek وdayOfMonth وtimeOfDay وtimezone وendDate وmaxOccurrences |
conversionTracking | object | لا | primaryConversion / secondaryConversions، اسم حدث وشروط خصائص، وconversionWindowHours وattributionModel، أي first_touch/last_touch/linear |
emailConfigId | string | لا | هوية مرسل Email محفوظة لقناة Email |
subscriptionCategoryId | string | لا | فئة الموافقة أو قائمة الاشتراك التي تُرسل الحملة ضمنها |
sendVolumeLimit | object | لا | enabled وmaxSends وcadence، أي lifetime/per_send |
محتوى رسالة In-app
تحمل كل نسخة in-app محتواها المنشأ في customContent. ويحدد mode الحقول المطبقة:
mode | الحقول | يُعرض بوصفه |
|---|---|---|
native | title وbody وimageUrl وbuttons وcloseButton وbackdropDismissible وstyle | مكوّنات التطبيق نفسه - دون web view |
html (الافتراضي) | html وcss | شيفرة كتبها المؤلف داخل web view |
drag_drop | html وcss وgrapejsData | مثل html، وgrapejsData هي حالة المحرر المرئي |
يمكن حذف mode، وعندها يعني html.
{
"name": "Weekend offer",
"channel": "in_app",
"channelConfig": { "type": "modal", "triggers": [] },
"variants": [
{
"id": "v1",
"name": "Native",
"weight": 100,
"customContent": {
"mode": "native",
"title": "Weekend only",
"body": "Hi {{ firstName }}, members get 20% off through Sunday.",
"imageUrl": "https://cdn.example.com/weekend.png",
"buttons": [
{ "id": "cta", "text": "See offer", "action": "url", "url": "https://example.com/offer" },
{ "id": "later", "text": "Not now", "action": "dismiss" }
],
"closeButton": true,
"backdropDismissible": true,
"style": {
"backgroundColor": "#0A1240",
"textColor": "#FFFFFF",
"primaryButtonColor": "#00C8B7",
"cornerRadius": 18
}
}
}
]
}
الحقول الأصلية نص وليست markup. تُسلَّم إلى التطبيق دون escaping لأن التطبيق يعرضها في عناصر نصية، فيصل A & B كما هو وليس A & B. يعمل التخصيص عبر Liquid في title وbody وفي text وurl للأزرار.
الحقل buttons محدود بثلاثة. والقيمة action واحدة من dismiss أو url أو deep_link؛ ويُشترط url للأخيرتين.
style - تجاوزات عرض اختيارية
كل حقل اختياري، والحقل الغائب يعني الوراثة: لون سطح التطبيق ولون نصه ولون تمييزه وخطه. هذه الوراثة هي جوهر المحتوى الأصلي، فأرسل الحقل فقط حين تحتاجه الحملة.
| الحقل | النوع | يسري على | المعنى |
|---|---|---|---|
backgroundColor | string | web وiOS وAndroid | خلفية البطاقة |
textColor | string | web وiOS وAndroid | العنوان والنص (النص أخف قليلًا) |
primaryButtonColor | string | web وiOS وAndroid | لون الزر الأول |
primaryButtonTextColor | string | web وiOS وAndroid | نص الزر. إن غاب اختير الأسود أو الأبيض حسب التباين مع اللون |
cornerRadius | number | web وiOS وAndroid | من 0 إلى 48. يُتجاهل في fullscreen حيث تكشف الزوايا المستديرة التطبيق خلفها |
fontSize | number | web وiOS وAndroid | من 10 إلى 32. حجم نص الرسالة، ويُشتق منه حجم العنوان. وعلى الهاتف يُطبَّق فوقه إعداد حجم النص لدى المستخدم |
titleWeight | string | web وiOS وAndroid | regular أو medium أو semibold أو bold. للعنوان فقط، ويبقى النص عاديًا لسهولة القراءة |
textAlign | string | web وiOS وAndroid | auto (الافتراضي) أو start أو center أو end. يتبع auto لغة الرسالة نفسها، فتُقرأ العربية والعبرية من اليمين داخل تطبيق بالإنجليزية |
fontFamily | string | الويب؛ وعلى الهاتف قدر الإمكان | يسري على الهاتف فقط إن كان التطبيق يرفق الخط (iOS: مسجَّل، وAndroid: res/font أو عائلة نظام). وإن غاب احتفظ التطبيق بخطه بدل استبداله |
customCss | string | الويب فقط | CSS مكتوب يدويًا، حتى 20000 حرف. تعيد حزمة الويب كتابة كل محدِّد ليقع داخل الرسالة قبل الحقن، فلا تصل أي قاعدة إلى الصفحة المضيفة، ويُحذف @import. ولا توجد في الهواتف محركات CSS |
تُمرَّر الألوان كما كُتبت: hex أو rgb() أو كلمة مفتاحية في CSS. والقيمة التي
يتعذّر تحليلها تعود إلى القيمة الموروثة بدل إفشال الرسالة.
في customCss يمكنك استهداف البطاقة نفسها وh2 وp وbutton.primary
وbutton.secondary، كما تُتاح الحقول أعلاه كمتغيرات CSS: --joryio-inapp-bg
و--joryio-inapp-fg و--joryio-inapp-primary و--joryio-inapp-primary-fg
و--joryio-inapp-radius و--joryio-inapp-font.
محتوى HTML يتطلب موافقة صريحة من التطبيق. ترفض حزم SDK للجوال والويب رسائل HTML داخل التطبيق ما لم يضبط التطبيق allowHtmlJsInAppMessages عند التهيئة، لأن تلك الرسالة تنفذ JavaScript كتبها المؤلف داخل التطبيق. أما المحتوى الأصلي فيُعرض دائمًا. راجع أدلة Android وiOS والويب.
| sendRateLimit | object | لا | enabled وmaxPerMinute |
| quietTimeOverride | object | لا | تجاوز وقت هادئ خاص بالحملة |
| utmSettings | object | لا | تجاوز UTM/وسم الروابط خاص بالحملة |
| channelConfig | object | لا | إعداد خاص بالقناة، ويعتمد شكله على القناة |
| stoConfig / abTestConfig | object | لا | إعداد تحسين وقت الإرسال أو اختبار A/B |
| resendPolicy | string | لا | سياسة الإرسال مجدداً لحملة لمرة واحدة: only_new، الافتراضي، أو everyone أو cooldown |
| resendCooldownDays | number | لا | نافذة الحداثة بالأيام لسياسة resendPolicy: "cooldown" |
| tags | string[] | لا | الوسوم |
| status | string | لا | draft أو scheduled أو active أو paused أو completed أو cancelled |
بنية المتغير
كل عنصر في variants:
| الحقل | النوع | مطلوب | الوصف |
|---|---|---|---|
id | string | نعم | معرّف المتغير |
name | string | نعم | اسم المتغير |
weight | number | نعم | حصة الحركة من 0 إلى 100؛ يجب أن يجمع الوزن وفق قواعد القناة |
message | object | لا | رسالة القناة. Email: subject وpreheader وfrom وfromName وhtml أو templateId وtext. SMS: body وfrom وshortenLinks. Push: title وbody وicon وimage وdata. WhatsApp: messageType، template/reply، وtemplateId وwabaId وvariableMapping وreplyText. Webhook: url وmethod وheaders وbody وauth وbodyType |
isControlGroup | boolean | لا | يعلّم المتغير كمجموعة ضابطة مستبعدة، لتمكين قياس الزيادة |
مثال لطلب
curl -X POST https://api-eu1.joryio.com/campaigns \
-H "Authorization: Bearer jry_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"name": "July Newsletter",
"channel": "email",
"sendType": "scheduled",
"scheduledAt": "2026-07-20T10:00:00.000Z",
"scheduledTimezone": "America/New_York",
"variants": [
{
"id": "variant-a",
"name": "Variant A",
"weight": 100,
"message": {
"subject": "Your July update",
"from": "news@example.com",
"fromName": "Example",
"html": "<h1>Hello {{ user.firstName }}</h1>"
}
}
],
"targeting": {
"filterGroups": [
{
"filters": [
{ "type": "attribute", "field": "plan", "operator": "equals", "value": "premium" }
],
"operator": "AND"
}
]
}
}'
الاستجابة
يعيد كائن الحملة التي أُنشئت:
{
"id": "8f14e45f-ceea-467f-a11d-2f4b6a1c9e3b",
"name": "July Newsletter",
"channel": "email",
"status": "scheduled",
"sendType": "scheduled",
"scheduledAt": "2026-07-20T14:00:00.000Z",
"variants": [ ... ],
"targeting": { ... },
"tags": [],
"createdAt": "2026-07-12T09:00:00.000Z",
"updatedAt": "2026-07-12T09:00:00.000Z"
}
عرض الحملات
نقطة النهاية
GET /campaigns
معاملات الاستعلام
| المعامل | النوع | الافتراضي | الوصف |
|---|---|---|---|
status | string | - | تصفية بالحالة؛ افصل الحالات المتعددة بفاصلة |
channel | string | - | تصفية بالقناة؛ افصل القنوات المتعددة بفاصلة |
tags | string | - | تصفية بالوسوم؛ مفصولة بفاصلة |
q | string | - | بحث نصي حر |
createdBy / editedBy | string | - | تصفية بالمنشئ أو آخر محرر؛ معرّفات مستخدمين مفصولة بفاصلة |
createdFrom | string | - | الحملات التي أُنشئت في أو بعد تاريخ ISO هذا |
page | number | 1 | رقم الصفحة |
limit | number | 20 | النتائج في الصفحة، بحد أقصى 100 |
مثال لطلب
curl -X GET "https://api-eu1.joryio.com/campaigns?status=active&channel=email&limit=50" \
-H "Authorization: Bearer jry_live_your_api_key"
الاستجابة
{
"data": [
{ "id": "8f14e45f-ceea-467f-a11d-2f4b6a1c9e3b", "name": "July Newsletter", "channel": "email", "status": "active" }
],
"pagination": {
"total": 23,
"page": 1,
"limit": 50,
"offset": 0,
"totalPages": 1,
"hasMore": false
}
}
الحصول على حملة
GET /campaigns/:campaignId
curl -X GET https://api-eu1.joryio.com/campaigns/8f14e45f-ceea-467f-a11d-2f4b6a1c9e3b \
-H "Authorization: Bearer jry_live_your_api_key"
يعيد كائن الحملة كاملاً. تكون حالة الحملة إحدى: draft أو scheduled أو active أو paused أو completed أو cancelled أو archived أو failed، أي خطأ إرسال دائم؛ راجع failureReason.
تحديث حملة
PUT /campaigns/:campaignId
يقبل النص الحقول نفسها في إنشاء حملة، وكلها اختيارية؛ لا تُحدَّث إلا الحقول المقدمة.
curl -X PUT https://api-eu1.joryio.com/campaigns/8f14e45f-ceea-467f-a11d-2f4b6a1c9e3b \
-H "Authorization: Bearer jry_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{ "name": "July Newsletter v2" }'
تبقى الحملات المشغلة والمتكررة active طوال عمرها. لا يغير تعديل حملة active ما يُرسل حالياً؛ بل تُخزن التعديلات كمسودة معلّقة. استدعِ POST /campaigns/:campaignId/publish لتطبيقها على الحملة الجارية بصورة ذرية، أو POST /campaigns/:campaignId/discard-draft للتخلص منها. أما حملات المسودة فتُحدّث في مكانها ولا تحتاج إلى نشر.
حذف حملة
DELETE /campaigns/:campaignId
يعيد 204 No Content. لا يُحذف نهائياً إلا المسودات التي لم تُشغّل قط؛ أما الحملات ذات سجل الإرسال فيجب أرشفتها، عبر POST /campaigns/:campaignId/archive، فيتوقف الإرسال مع الحفاظ على التحليلات.
دورة حياة الحملة
| الطريقة | المسار | الوصف |
|---|---|---|
POST | /campaigns/:campaignId/send | إطلاق الحملة، بدء الإرسال أو تفعيل حملة مشغلة |
POST | /campaigns/:campaignId/pause | إيقاف حملة جارية مؤقتاً |
POST | /campaigns/:campaignId/resume | استئناف حملة متوقفة |
POST | /campaigns/:campaignId/publish | تطبيق التعديلات المخزنة على حملة نشطة بصورة ذرية؛ 400 إن لم توجد تعديلات |
POST | /campaigns/:campaignId/discard-draft | التخلص من التعديلات المخزنة على حملة نشطة؛ لا تأثير إن لم توجد |
POST | /campaigns/:campaignId/cancel | إلغاء حملة |
POST | /campaigns/:campaignId/resend | إعادة إرسال حملة لمرة واحدة مكتملة وفق resendPolicy |
POST | /campaigns/:campaignId/retry | إعادة محاولة حملة failed وإعادتها إلى scheduled |
POST | /campaigns/:campaignId/preview-launch | ملخص قبل الإطلاق، حجم الجمهور والفحوصات، من دون إرسال |
POST | /campaigns/:campaignId/duplicate | تكرار حملة |
POST | /campaigns/:campaignId/archive | أرشفة، توقف الإرسال وتحفظ السجل |
POST | /campaigns/:campaignId/unarchive | استعادة إلى حالة لا ترسل؛ استأنف صراحة للإرسال مجدداً |
POST | /campaigns/:campaignId/stop-recurring | إيقاف الوقائع المستقبلية لحملة متكررة |
curl -X POST https://api-eu1.joryio.com/campaigns/8f14e45f-ceea-467f-a11d-2f4b6a1c9e3b/send \
-H "Authorization: Bearer jry_live_your_api_key"
يمكن إعادة إرسال حملة مكتملة لمرة واحدة بواسطة POST /campaigns/:campaignId/resend. ويحدد resendPolicy من يتلقاها:
only_new، الافتراضي: يتجاوز كل من تلقاها بالفعل، فلا تصل إلا إلى مستخدمين لم يصلهم الإرسال.everyone: يعيد الإرسال إلى الجمهور كله، بما فيه المستلمون السابقون.cooldown: يعيد الإرسال إلى الجميع عدا من وصلتهم رسالة في آخرresendCooldownDaysيوماً.
تُفرض الموافقة وقوائم الحظر دائماً. تتبع حدود التكرار علامة ignoreTouchingRules للحملة. تستخدم الحملات المشغلة triggerConfig.reEntry بدلاً من ذلك، وتعيد الحملات المتكررة الإرسال وفق جدولها. يعيد النظام 400 لهذه الأنواع أو لـ in-app أو لحملة لم تنته من الإرسال.
إحصاءات الحملة
GET /campaigns/:campaignId/stats
معاملات الاستعلام: startDate وendDate، وهما ISO 8601 واختياريان.
الاستجابة
{
"campaignId": "8f14e45f-ceea-467f-a11d-2f4b6a1c9e3b",
"name": "July Newsletter",
"channel": "email",
"status": "completed",
"stats": {
"queued": 1200,
"sent": 1180,
"delivered": 1150,
"failed": 30,
"opened": 640,
"clicked": 210
},
"conversionStats": { ... },
"revenue": { ... },
"uplift": null,
"conversionTracking": { ... },
"startedAt": "2026-07-20T14:00:00.000Z",
"completedAt": "2026-07-20T14:12:00.000Z",
"createdAt": "2026-07-12T09:00:00.000Z"
}
تُملأ conversionStats وrevenue عند ضبط تتبع التحويل، ولا تُملأ uplift إلا إذا عُلّم متغير بـ isControlGroup.
نقاط نهاية إحصاءات ذات صلة
| الطريقة | المسار | الوصف |
|---|---|---|
GET | /campaigns/:campaignId/variant-stats | إحصاءات A/B لكل متغير مع الدلالة الإحصائية، startDate وendDate |
GET | /campaigns/:campaignId/links | إحصاءات نقر الروابط، startDate وendDate |
GET | /campaigns/:campaignId/failure-reasons | فشل التسليم مجمعاً برمز خطأ DLR، [{ code, reason, count }] |
GET | /campaigns/:campaignId/recipients | مستلمون مقسمون إلى صفحات مع آخر حالة رسالة، status وlimit بحد 200 وoffset |
GET | /campaigns/:campaignId/recipients/:userId | خط زمني لأحداث رسالة هذا المستخدم في الحملة |
GET | /campaigns/:campaignId/in-app-stats | إحصاءات عرض In-app لحملات In-app. تتضمّن displayFrequency - توزيع مرات العرض لكل مستخدم بالشكل { tailBucket, buckets: [{ displays, users, impressions }], maxPerUser }. القيم التي تساوي tailBucket أو تزيد عنه تُجمَّع في سلّة واحدة، لذا فإن displays === tailBucket تعني "هذا العدد أو أكثر"؛ لحساب المتوسط استخدم impressions لكل سلّة (وليس displays * users)، وmaxPerUser للمستخدم الأكثر تعرّضًا |
GET | /campaigns/:campaignId/impressions | قائمة مرات ظهور In-app، limit وoffset وstartDate وendDate |
إرسال رسالة معاملية
أرسل رسالة لمرة واحدة إلى مستخدم واحد من دون إنشاء حملة.
POST /campaigns/transactional/send
نص الطلب
| الحقل | النوع | مطلوب | الوصف |
|---|---|---|---|
userId | string | نعم | معرّف المستخدم المستهدف |
channel | string | نعم | email أو sms أو push أو viber |
message | object | نعم | subject لـ Email وbody للنص العادي وhtml لـ Email. يمكن أن يكون {} لـ viber؛ فنص القالب الموافق عليه هو الرسالة |
viberTemplateId | string | Viber فقط | قالب موافق عليه من سجل قوالب Viber. يفرض Rakuten Viber قوالب معتمدة مسبقاً للرسائل المعاملية وOTP منذ يوليو 2026؛ ويُحاسب المحتوى غير المعتمد بسعر ترويجي، لذلك يرفض API الإرسال من دونه |
variables | object | لا | Viber فقط؛ قيم الحقول الديناميكية للقالب، وتُدمج في سياق التخصيص |
triggerData | object | لا | سياق متاح للتخصيص: type وname وproperties وmetadata |
idempotencyKey | string | لا | مفتاح إزالة تكرار يورده المستدعي؛ لا يُسلّم طلب معاد بالمفتاح نفسه مرتين |
curl -X POST https://api-eu1.joryio.com/campaigns/transactional/send \
-H "Authorization: Bearer jry_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"userId": "user_123",
"channel": "email",
"message": {
"subject": "Your receipt",
"html": "<p>Thanks for your order, {{ user.firstName }}.</p>"
},
"idempotencyKey": "order-98421-receipt"
}'
نقاط نهاية إضافية
| الطريقة | المسار | الوصف |
|---|---|---|
POST | /campaigns/preview | معاينة مستخدمين يطابقون معايير الاستهداف؛ النص: targeting وlimit اختياري بحد 500 وchannel وwebAppId |
POST | /campaigns/spam-check | فحص محتوى Email ضد الرسائل المزعجة قبل الإرسال |
GET | /campaigns/:campaignId/versions | عرض لقطات الإصدارات المحفوظة |
GET | /campaigns/:campaignId/versions/compare?v1=&v2= | مقارنة إصدارين |
GET | /campaigns/:campaignId/versions/:versionId | الحصول على لقطة إصدار |
POST | /campaigns/:campaignId/versions/:versionId/rollback | الرجوع إلى إصدار |
GET | /campaigns/:campaignId/history | سجل التدقيق، limit بحد 200 |
POST | /campaigns/bulk-delete / bulk-duplicate / bulk-archive / bulk-unarchive | إجراءات جماعية؛ النص { "ids": [...] } ويعيد { succeeded, failed } |
POST | /campaigns/bulk-tag | وسم جماعي؛ النص { "ids": [...], "tags": [...] } |
POST | /campaigns/:campaignId/send-test-whatsapp / send-test-sms / send-test-push / send-test-in-app | إرسالات اختبار إلى هاتف أو مستخدم قبل الإطلاق |
GET | /campaigns/:campaignId/sto-coverage | تغطية تحسين وقت الإرسال للجمهور |
GET | /campaigns/:campaignId/recurring-status | حالة الحملة المتكررة |
POST | /campaigns/:campaignId/retest-ab | إعادة ضبط فائز A/B لحملة متكررة تستخدم المتغير الفائز |
POST | /campaigns/:campaignId/launch-rl | إطلاق وضع التحسين بالذكاء الاصطناعي، RL |
GET | /campaigns/:campaignId/rl-stats | إحصاءات لوحة حملة محسنة بالذكاء الاصطناعي |
POST | /campaigns/:campaignId/pause-rl / resume-rl | إيقاف أو استئناف حملة محسنة بالذكاء الاصطناعي |
POST | /campaigns/ml-path-warmth | حالة إحماء التخصيص لمعرّفات المسار أو المتغير |
استجابات الأخطاء
تشترك كل الأخطاء في الشكل القياسي. راجع استجابة الخطأ في نظرة API العامة للتنسيق وقائمة رموز الحالة كاملة.
{
"statusCode": 404,
"message": "Campaign not found",
"timestamp": "2026-07-12T09:00:00.000Z",
"path": "/campaigns/8f14e45f-ceea-467f-a11d-2f4b6a1c9e3b"
}
| الحالة | متى تحدث |
|---|---|
400 | فشل التحقق، مثل channel غير صالح أو weight مفقود لمتغير؛ يضيف النص مصفوفة errors برسالة لكل حقل فاشل |
401 | مفتاح API مفقود أو غير صالح |
403 | لا يحمل مفتاح API نطاق campaigns:* المطلوب |
404 | الحملة غير موجودة في مساحة العمل |
409 | تعارض في دورة الحياة، مثل استئناف حملة لم تعد متوقفة، أو إرسال حملة لم تعد في حالة قابلة للإرسال |
ذو صلة
- إنشاء الحملات
- تحليلات الحملات
- Segments API - أنشئ الجماهير التي تستهدفها الحملات
- Canvas API - رحلات متعددة الخطوات