تخطّ إلى المحتوى الرئيسي

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

نص الطلب

الحقلالنوعمطلوبالوصف
namestringنعماسم الحملة، بحد أقصى 255 حرفاً
descriptionstringلاالوصف، بحد أقصى 1000 حرف
channelstringنعمemail أو sms أو viber أو push أو webhook أو whatsapp أو in_app أو ai_optimized
variantsarrayنعم*متغيرات الرسالة؛ مطلوبة لكل قناة عدا in_app
targetingobjectلاالجمهور: userIds وfilterGroups وexcludeFilterGroups وfilterOperator وsubscriptionPreference
sendTypestringلاimmediate أو scheduled أو triggered أو intelligent أو recurring أو ai_optimized
scheduledAtstringلاتاريخ ISO 8601 لـ sendType: "scheduled"
scheduledTimezonestringلاالمنطقة الزمنية IANA التي يفسر بها الوقت المجدول
triggerConfigobjectلاقاعدة تشغيل لـ sendType: "triggered"، وتشمل type وeventName وconditions وreEntry وcooldownHours
recurringScheduleobjectلالـ sendType: "recurring": frequency، أي daily/weekly/monthly/custom، وcron وdayOfWeek وdayOfMonth وtimeOfDay وtimezone وendDate وmaxOccurrences
conversionTrackingobjectلاprimaryConversion / secondaryConversions، اسم حدث وشروط خصائص، وconversionWindowHours وattributionModel، أي first_touch/last_touch/linear
emailConfigIdstringلاهوية مرسل Email محفوظة لقناة Email
subscriptionCategoryIdstringلافئة الموافقة أو قائمة الاشتراك التي تُرسل الحملة ضمنها
sendVolumeLimitobjectلاenabled وmaxSends وcadence، أي lifetime/per_send

محتوى رسالة In-app

تحمل كل نسخة in-app محتواها المنشأ في customContent. ويحدد mode الحقول المطبقة:

modeالحقوليُعرض بوصفه
nativetitle وbody وimageUrl وbuttons وcloseButton وbackdropDismissible وstyleمكوّنات التطبيق نفسه - دون web view
html (الافتراضي)html وcssشيفرة كتبها المؤلف داخل web view
drag_drophtml و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 - تجاوزات عرض اختيارية

كل حقل اختياري، والحقل الغائب يعني الوراثة: لون سطح التطبيق ولون نصه ولون تمييزه وخطه. هذه الوراثة هي جوهر المحتوى الأصلي، فأرسل الحقل فقط حين تحتاجه الحملة.

الحقلالنوعيسري علىالمعنى
backgroundColorstringweb وiOS وAndroidخلفية البطاقة
textColorstringweb وiOS وAndroidالعنوان والنص (النص أخف قليلًا)
primaryButtonColorstringweb وiOS وAndroidلون الزر الأول
primaryButtonTextColorstringweb وiOS وAndroidنص الزر. إن غاب اختير الأسود أو الأبيض حسب التباين مع اللون
cornerRadiusnumberweb وiOS وAndroidمن 0 إلى 48. يُتجاهل في fullscreen حيث تكشف الزوايا المستديرة التطبيق خلفها
fontSizenumberweb وiOS وAndroidمن 10 إلى 32. حجم نص الرسالة، ويُشتق منه حجم العنوان. وعلى الهاتف يُطبَّق فوقه إعداد حجم النص لدى المستخدم
titleWeightstringweb وiOS وAndroidregular أو medium أو semibold أو bold. للعنوان فقط، ويبقى النص عاديًا لسهولة القراءة
textAlignstringweb وiOS وAndroidauto (الافتراضي) أو start أو center أو end. يتبع auto لغة الرسالة نفسها، فتُقرأ العربية والعبرية من اليمين داخل تطبيق بالإنجليزية
fontFamilystringالويب؛ وعلى الهاتف قدر الإمكانيسري على الهاتف فقط إن كان التطبيق يرفق الخط (iOS: مسجَّل، وAndroid: res/font أو عائلة نظام). وإن غاب احتفظ التطبيق بخطه بدل استبداله
customCssstringالويب فقط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:

الحقلالنوعمطلوبالوصف
idstringنعممعرّف المتغير
namestringنعماسم المتغير
weightnumberنعمحصة الحركة من 0 إلى 100؛ يجب أن يجمع الوزن وفق قواعد القناة
messageobjectلارسالة القناة. 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
isControlGroupbooleanلايعلّم المتغير كمجموعة ضابطة مستبعدة، لتمكين قياس الزيادة

مثال لطلب

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

معاملات الاستعلام

المعاملالنوعالافتراضيالوصف
statusstring-تصفية بالحالة؛ افصل الحالات المتعددة بفاصلة
channelstring-تصفية بالقناة؛ افصل القنوات المتعددة بفاصلة
tagsstring-تصفية بالوسوم؛ مفصولة بفاصلة
qstring-بحث نصي حر
createdBy / editedBystring-تصفية بالمنشئ أو آخر محرر؛ معرّفات مستخدمين مفصولة بفاصلة
createdFromstring-الحملات التي أُنشئت في أو بعد تاريخ ISO هذا
pagenumber1رقم الصفحة
limitnumber20النتائج في الصفحة، بحد أقصى 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

نص الطلب

الحقلالنوعمطلوبالوصف
userIdstringنعممعرّف المستخدم المستهدف
channelstringنعمemail أو sms أو push أو viber
messageobjectنعمsubject لـ Email وbody للنص العادي وhtml لـ Email. يمكن أن يكون {} لـ viber؛ فنص القالب الموافق عليه هو الرسالة
viberTemplateIdstringViber فقطقالب موافق عليه من سجل قوالب Viber. يفرض Rakuten Viber قوالب معتمدة مسبقاً للرسائل المعاملية وOTP منذ يوليو 2026؛ ويُحاسب المحتوى غير المعتمد بسعر ترويجي، لذلك يرفض API الإرسال من دونه
variablesobjectلاViber فقط؛ قيم الحقول الديناميكية للقالب، وتُدمج في سياق التخصيص
triggerDataobjectلاسياق متاح للتخصيص: type وname وproperties وmetadata
idempotencyKeystringلامفتاح إزالة تكرار يورده المستدعي؛ لا يُسلّم طلب معاد بالمفتاح نفسه مرتين
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تعارض في دورة الحياة، مثل استئناف حملة لم تعد متوقفة، أو إرسال حملة لم تعد في حالة قابلة للإرسال

ذو صلة