نظرة عامة على Canvas API
أدر رحلات Canvas، وهي أتمتات متعددة الخطوات تُنشأ في منشئ الرحلات المرئي، عبر REST API.
جميع نقاط النهاية في هذه الصفحة نسبية إلى عنوان الأساس: https://api-eu1.joryio.com/api. راجع نظرة عامة على API.
توجد نقاط نهاية Canvas تحت المسار /journeys: اسم مورد API لـ canvas هو journey. يشير canvasId ومعرّف الرحلة إلى المعرّف نفسه.
المصادقة
تتطلب جميع الطلبات مصادقة مفتاح API:
Authorization: Bearer jry_live_your_api_key_here
Content-Type: application/json
النطاقات المطلوبة لكل مجموعة نقاط نهاية:
| النطاق | نقاط النهاية |
|---|---|
canvas:read | السرد والجلب والتنفيذات والإحصاءات وتحليلات العُقد والإصدارات |
canvas:write | الإنشاء والتحديث والتكرار والأرشفة والوسوم والنسخ والتجارب |
canvas:activate | التفعيل والإيقاف المؤقت والاستئناف والنشر وإعادة التشغيل وإدخال مستخدم والتراجع |
canvas:enter | إدخال مستخدم (مشغل API) - ولا شيء غير ذلك. مشمولة في canvas:activate |
canvas:delete | الحذف والحذف المجمع |
سرد الرحلات
GET /journeys
معاملات الاستعلام
| المعامل | النوع | الافتراضي | الوصف |
|---|---|---|---|
status | string | - | الفلترة حسب الحالة |
tags | string | - | الفلترة حسب الوسوم، مفصولة بفواصل |
q | string | - | بحث نص حر |
createdBy / editedBy | string | - | الفلترة حسب المنشئ/آخر محرر، معرّفات مستخدمين مفصولة بفواصل |
page | number | 1 | رقم الصفحة |
limit | number | - | النتائج في الصفحة، حتى 100 |
طلب مثال
curl -X GET "https://api-eu1.joryio.com/api/journeys?status=active&limit=20" \
-H "Authorization: Bearer jry_live_your_api_key"
الاستجابة
{
"data": [
{ "id": "3c9d2f1a-5e8b-4a7c-9f0d-1b2a3c4d5e6f", "name": "Welcome journey", "status": "active" }
],
"pagination": {
"total": 8,
"page": 1,
"limit": 20,
"offset": 0,
"totalPages": 1,
"hasMore": false
}
}
إنشاء رحلة
POST /journeys
نص الطلب
| الحقل | النوع | مطلوب | الوصف |
|---|---|---|---|
name | string | نعم | اسم الرحلة، بحد أقصى 255 حرفًا |
description | string | لا | الوصف، بحد أقصى 1000 حرف |
nodes | array | لا | عُقد الرسم البياني، بحد أقصى 500. قد تبدأ المسودة المنشأة بالمعالج فارغة |
edges | array | لا | حواف الرسم البياني، بحد أقصى 1000 |
entryTrigger | object | لا | كيفية دخول المستخدمين، راجع أدناه |
variants | array | لا | نسخ A/B/n للرحلة كاملة، راجع أدناه |
settings | object | لا | timezone وquietTime وconversionTracking وreEntryPolicy وpersonalizedVariants مع الإعداد |
sendType | string | لا | immediate أو scheduled أو recurring أو trigger |
scheduledAt | string | لا | تاريخ ISO 8601 لـ sendType: "scheduled" |
recurringSchedule | object | لا | frequency (daily/weekly/monthly/custom) وcron وdayOfWeek وdayOfMonth وtimeOfDay وtimezone وendDate وmaxOccurrences timeOfDay بصيغة HH:MM بنظام 24 ساعة (09:00، 17:30)؛ وتُرفض أي قيمة أخرى بالرمز 400. |
targeting | object | لا | جمهور الرحلات المجدولة/الفورية: userIds وfilterGroups وexcludeFilterGroups وfilterOperator وsubscriptionPreference |
exitCriteria | object | لا | شكل الفلاتر نفسه لـ targeting؛ ويُخرج المستخدمون النشطون المطابقون |
tags | string[] | لا | الوسوم |
بنية العُقدة
يحمل كل إدخال في nodes حقول id وtype وconfig الخاص بالنوع وposition اختياريًا (x/y للمحرر). قيم type الصالحة:
trigger, message, in_app_message, delay, condition, behavior_split,
context, update_user, update_wallet, connector, experiment,
ai_decision, ai_agent
هناك عقدة message واحدة؛ والقناة هي config.channel (email وsms وpush وwhatsapp وviber وin_app وwebhook). أما أنواع العقد القديمة لكل قناة - email وsms وpush وwhatsapp وwhatsapp_message وwallet وwebhook والاسم البديل in_app - فقد أُزيلت وصارت مرفوضة عند الإنشاء والتحديث.
يحمل كل إدخال في edges حقول id وsource وtarget وsourceHandle / targetHandle / label اختيارية. يختار sourceHandle منفذ الإخراج في العُقد متعددة المخرجات، مثل مجموعات الفروع أو did / timed_out في behavior split.
مشغّل الدخول
يحمل entryTrigger حقل type وإعداد config خاصًا بالنوع. الأنواع الصالحة: event وapi وentity_change وwhatsapp_inbound وsms_inbound وviber_inbound وattribute_change وsubscription_status وschedule.
دخول الشريحة ليس مشغّلًا. للدخول الناتج عن شيء فعله الشخص، استخدم مشغّل حدث مع فلتر؛ وللدخول الناتج عن مرور الوقت، استخدم رحلة متكررة على الشريحة.
نسخ الرحلة كاملة
يحمل كل إدخال في variants: id وname وpercentage (من 0 إلى 100، ويجب أن يكون مجموع كل النسخ 100) وisControlGroup، إذ إن نسخة التحكم مجموعة محجوبة مقاسة بلا مسار، وtriggerNodeId، أي عُقدة المشغّل التي تنطلق منها نسخة المعالجة. راجع تحليلات الرحلات لطريقة عرض نتائج النسخ. كان اسم الحقل سابقًا isControl، ولا يزال هذا الاسم مقبولًا عند الكتابة ويُحوَّل إلى isControlGroup.
طلب مثال
curl -X POST https://api-eu1.joryio.com/api/journeys \
-H "Authorization: Bearer jry_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"name": "Welcome journey",
"sendType": "trigger",
"entryTrigger": {
"type": "event",
"config": { "eventName": "signed_up" }
},
"nodes": [
{ "id": "n1", "type": "trigger", "config": { "type": "event", "eventName": "signed_up" } },
{ "id": "n2", "type": "delay", "config": { "delayType": "duration", "value": 1, "unit": "days" } },
{ "id": "n3", "type": "email", "config": { "subject": "Welcome!", "html": "<p>Hi {{ user.firstName }}</p>" } }
],
"edges": [
{ "id": "e1", "source": "n1", "target": "n2" },
{ "id": "e2", "source": "n2", "target": "n3" }
]
}'
يعيد كائن الرحلة المُنشأ بحالة draft.
جلب رحلة
GET /journeys/:canvasId
curl -X GET https://api-eu1.joryio.com/api/journeys/3c9d2f1a-5e8b-4a7c-9f0d-1b2a3c4d5e6f \
-H "Authorization: Bearer jry_live_your_api_key"
يعيد الرحلة كاملة، بما فيها nodes وedges وentryTrigger وvariants وsettings.
تحديث رحلة
PUT /journeys/:canvasId
يقبل النص الحقول نفسها في إنشاء رحلة؛ كلها اختيارية ولا تُحدّث إلا الحقول المقدمة.
curl -X PUT https://api-eu1.joryio.com/api/journeys/3c9d2f1a-5e8b-4a7c-9f0d-1b2a3c4d5e6f \
-H "Authorization: Bearer jry_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{ "name": "Welcome journey v2" }'
حذف رحلة
DELETE /journeys/:canvasId
يعيد 204 No Content. يفضّل أرشفة الرحلات ذات سجل التشغيل بدل حذفها عبر POST /journeys/:canvasId/archive، إذ توقف الأرشفة التسجيل والإرسال مع الاحتفاظ بالتحليلات.
نقاط نهاية الحالة
| الطريقة | المسار | النطاق | الوصف |
|---|---|---|---|
POST | /journeys/:canvasId/activate | canvas:activate | تفعيل؛ يمكن للمستخدمين البدء بالدخول |
POST | /journeys/:canvasId/pause | canvas:activate | إيقاف مؤقت؛ يوقف الإدخالات الجديدة والتقدم |
POST | /journeys/:canvasId/resume | canvas:activate | استئناف رحلة موقفة |
POST | /journeys/:canvasId/archive | canvas:write | أرشفة، توقف التسجيل/الإرسال وتحفظ السجل |
POST | /journeys/:canvasId/unarchive | canvas:write | استعادة إلى حالة غير مشغلة؛ فعّل صراحة للاستئناف |
POST | /journeys/:canvasId/publish | canvas:activate | نشر المسودة الحالية كإصدار جديد. النص: changeSummary اختياري وuserTransition (keep_on_version / force_exit / migrate_to_new). فقط لرحلة مباشرة بالفعل (status: active)؛ وإلا 409 مع code: "journey_not_live" - يتم التفعيل الأول عبر POST /journeys/:canvasId/activate. |
POST | /journeys/:canvasId/rerun | canvas:activate | إعادة تشغيل الإصدار المنشور الحالي دون إنشاء إصدار جديد |
curl -X POST https://api-eu1.joryio.com/api/journeys/3c9d2f1a-5e8b-4a7c-9f0d-1b2a3c4d5e6f/activate \
-H "Authorization: Bearer jry_live_your_api_key"
إدخال مستخدم (مشغّل API)
سجّل مستخدمًا محددًا في رحلة؛ وهو مسار الإدخال لـ entryTrigger.type: "api".
POST /journeys/:canvasId/enter/:userId
الصلاحية: canvas:enter (مشمولة في canvas:activate)، كي يتمكن تكامل مثل Zapier من إضافة أشخاص إلى رحلة دون القدرة على تشغيلها أو إيقافها مؤقتًا أو تعديلها.
النص اختياري: context، وهو كائن JSON متاح للتنفيذ ويمكن قراءته في الرسائل والشروط كمتغيرات سياق الرحلة.
curl -X POST https://api-eu1.joryio.com/api/journeys/3c9d2f1a-5e8b-4a7c-9f0d-1b2a3c4d5e6f/enter/user_123 \
-H "Authorization: Bearer jry_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{ "context": { "source": "crm-sync", "priority": "high" } }'
قائمة الرحلات التي يمكن إدخال شخص إليها
GET /journeys/enterable?q=
الرحلات النشطة (active) بالشكل { "data": [{ "id", "name", "activatedAt" }] }، مرتبة حسب الاسم، 100 كحد أقصى؛ ويرشّح q بجزء من الاسم. الصلاحية: canvas:enter (أو canvas:read) - يمكن للتكامل المسموح له بإضافة أشخاص إلى الرحلات اختيار رحلة دون قراءة الرحلات نفسها.
التنفيذات والتحليلات
| الطريقة | المسار | الوصف |
|---|---|---|
GET | /journeys/:canvasId/executions | سرد التنفيذات، status وlimit حتى 100 وoffset |
GET | /journeys/:canvasId/executions/:executionId | جلب تنفيذ واحد. تحمل كل خطوة Webhook في history الحقل webhook (الحالة، النجاح، المدة، المحاولات، الخطأ، الحقول المحفوظة)؛ ويضيف الاستدعاء الفاشل (ليس 2xx) الحقل webhook.response ({ status, contentType, body, truncated }) - مع إخفاء الأسرار، وأول 2 KB من النص، ويُحفظ 30 يومًا، ولا يُعاد إلا لمن يملك pii:view (ومفتاح API فقط مع هذه الصلاحية؛ ويحصل الآخرون على webhook.responseHidden: true). لا يُخزَّن نص الاستجابة الناجحة أبدًا |
GET | /journeys/:canvasId/executions/:executionId/context | متغيرات سياق التنفيذ مع الأنواع المستنتجة |
GET | /journeys/:canvasId/stats | إحصاءات مستوى الرحلة، startDate وendDate |
GET | /journeys/:canvasId/node-analytics | تحليلات لكل عُقدة، startDate وendDate |
GET | /journeys/:canvasId/variant-stats | أداء نسخ الرحلة كاملة، A/B/n والتحكم |
GET | /journeys/:canvasId/recovered-carts | السلال المتروكة التي استعادتها الرحلة - القائمة التي خلف revenue.recoveredCarts في /stats، من الاستعلام نفسه، لذا فإن total يساوي ذلك العدد للقيم نفسها من startDate/endDate (السلال المستعادة ضمن النطاق؛ النهاية بتاريخ فقط تشمل اليوم كله، بالمنطقة الزمنية لمساحة العمل). limit من 1 إلى 100 (الافتراضي 25)، وoffset؛ الأحدث أولًا. الصفوف: orderNumber (رقم المتجر)، placedAt، recoveredAt، match (recovery_link، order = السلة نفسها، reported)، cartValue/cartCurrency، creditedRevenue/creditedCurrency (صافٍ بعد المبالغ المستردة)، contact (صاحب السلة) وbuyer (فقط عندما سُجّل الطلب باسم شخص آخر)؛ تُخفى أسماء جهات الاتصال من دون pii:view. يُرجع { data, total, limit, offset, summary } |
GET | /journeys/:canvasId/experiments/:nodeId/stats | إحصاءات عُقدة التجربة مع اختبار الدلالة |
POST | /journeys/:canvasId/experiments/:nodeId/declare-winner | إعلان فائز، النص pathId |
POST | /journeys/:canvasId/experiments/:nodeId/reset | إعادة تجربة إلى جمع البيانات |
GET | /journeys/:canvasId/personalization-status | حالة مرحلة النسخ المخصصة |
نقاط نهاية إضافية
| الطريقة | المسار | الوصف |
|---|---|---|
PUT | /journeys/:canvasId/variants | ضبط نسخ الرحلة كاملة، نص variants |
GET | /journeys/:canvasId/versions | سرد الإصدارات |
GET | /journeys/:canvasId/versions/compare?v1=&v2= | مقارنة إصدارين |
GET | /journeys/:canvasId/versions/compare-draft | مقارنة المسودة الحالية بآخر إصدار منشور |
GET | /journeys/:canvasId/versions/migration-preview | معاينة توافق التنفيذ قبل نشر مع ترحيل |
GET | /journeys/:canvasId/versions/:versionId | جلب إصدار |
POST | /journeys/:canvasId/versions/:versionId/rollback | التراجع إلى إصدار. للرحلات المباشرة فقط؛ وإلا 409 journey_not_live. |
GET | /journeys/:canvasId/versions/:versionId/stats | إحصاءات خاصة بإصدار |
GET | /journeys/:canvasId/versions/:versionId/executions | تنفيذات إصدار محدد |
GET | /journeys/:canvasId/version-execution-counts | أعداد التنفيذات لكل إصدار |
POST | /journeys/:canvasId/versions/:versionId/migrate | ترحيل التنفيذات المتوافقة قسرًا إلى إصدار |
GET | /journeys/:canvasId/history | سجل التدقيق، limit حتى 200 |
POST | /journeys/bulk-delete / bulk-duplicate / bulk-archive / bulk-unarchive | إجراءات مجمعة؛ النص { "ids": [...] } ويعيد { succeeded, failed } |
POST | /journeys/bulk-tag | إضافة وسوم مجمعة؛ النص { "ids": [...], "tags": [...] } |
POST | /journeys/webhook/test | تشغيل إعداد عُقدة Webhook مرة وإرجاع الاستجابة الحية مع الطلب المرسل (بيانات الاعتماد مخفية). يعرض user.userId جهة اتصال حقيقية من مساحة العمل تمامًا كالإرسال الفعلي، وإلا تُستخدم قيم عينة، مع تحديد للمعدل |
POST | /journeys/preview-context-value | عرض تعبير Liquid بسياق عينة |
POST | /journeys/preview-user-updates | تشغيل تجريبي لصفوف عُقدة Update User على مستخدم عينة |
POST | /journeys/preview-user-update-json | تشغيل تجريبي لـ JSON المخصص في عُقدة Update User: المدخل { json, sample } والمخرج { errors, preview: { errors, result } }. تمنع errors الحفظ أو التفعيل؛ وpreview هو الـ JSON معروضًا على العينة. لا يكتب شيئًا |
POST | /journeys/:canvasId/nodes/:nodeId/send-test-email / send-test-sms / send-test-whatsapp / send-test-push | إرسال اختباري لعُقدة رسالة |
POST | /journeys/:canvasId/cleanup-stuck-executions | تنظيف التنفيذات العالقة |
GET | /journeys/:canvasId/executions/:executionId/rendered-message/:nodeId | الرسالة المعروضة لتنفيذ وعُقدة محددين |
مراجعة الإطلاق
المراجعة نفسها التي تسبق الإرسال في الحملات (انظر دليل مراجعة الإطلاق)، تعمل على كل عقدة رسالة: رابط إلغاء الاشتراك، حقول الدمج، توثيق المرسل، الروابط غير الصالحة، طول الموضوع، النسخة النصية المفقودة، ساعات الهدوء، إضافة إلى node_unreachable للعقد التي لا يصل إليها أي مشغّل، وcart_variable_no_cart_trigger لرسالة تقرأ cart في رحلة لا تبدأ بحدث سلة، وchannel_not_set_up لقناة تستخدمها الرحلة وليس لها مزوّد أو مُرسِل (params.channel؛ عائق، أو توصية مع params.sandbox: 1 في مساحة عمل تجريبية حيث تُحاكى الإرسالات للجميع). يشغّلها التفعيل والنشر أيضًا.
GET /journeys/{canvasId}/launch-review
النطاق: canvas:read. للاستجابة البنية الموثقة في واجهة الحملات؛ تحمل نتيجة العقدة scope.kind = "node" وscope.id = معرّف العقدة وfix.nodeId.
عندما تتطلب مساحة العمل قائمة نظيفة (settings.launchReview.requireClean، افتراضيًا true) وتوجد عوائق، يجيب POST /journeys/{canvasId}/activate وPOST /journeys/{canvasId}/publish بـ 409 مع code: "launch_review_blocked" والعوائق في الجسم.
استجابات الأخطاء
تشترك جميع الأخطاء في الشكل القياسي. راجع استجابة الخطأ في نظرة API العامة لمعرفة التنسيق ورموز الحالة وسلوك تحديد المعدل.
{
"statusCode": 404,
"message": "Canvas with ID 8f14e45f-ceea-467f-a11d-2f4b6a1c9e3b not found",
"timestamp": "2026-07-12T09:00:00.000Z",
"path": "/journeys/8f14e45f-ceea-467f-a11d-2f4b6a1c9e3b"
}