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

E-Commerce API

أدر كتالوج المنتجات وتتبّع الطلبات وراقب نشاط السلات وحلل شرائح عملاء RFM.

كل نقاط النهاية في هذه الصفحة نسبية إلى عنوان الأساس https://api-eu1.joryio.com/api. راجع نظرة عامة على API.

الأذونات​

تتطلب قراءة بيانات الكتالوج والسلة والطلبات analytics:read. ويتطلب إنشاء المنتجات والسلات والطلبات أو تحديثها ecommerce:write. وكلاهما قابل للمنح لمفتاح API من الإعدادات ← مفاتيح API.

كانت مسارات الكتابة تتطلب سابقًا integrations:write، وهو غير معروض لمفاتيح API لأنه يحكم أيضًا مصادقة OAuth للموصّلات وإعدادات مزامنة المستودع. ولا تزال هذه المسارات تقبله، لذا لا يتأثر المستدعون الحاليون.

اصطلاح تسمية المعرّفات​

يستخدم Joryio اصطلاحاً واضحاً للمعرّفات.

معرّفاتك، استخدمها عند إنشاء البيانات أو تتبعها​

الحقلالوصفالمثال
productIdمعرّف منتجك، SKU أو معرّف منصة التجارة الإلكترونية"SKU-12345"
orderIdمعرّف طلبك، رقم الطلب من منصتك"ORD-2024-001"
userIdمعرّف المستخدم لديك"user-123"

معرّفات Joryio، تعاد في استجابات API​

الحقلالوصفمتى تستخدمه
idمعرّف Joryio الداخلي للمنتجات أو الطلباتيُعاد في استجابات API؛ استخدمه للتحديث أو الحذف
joryioUserIdمعرّف المستخدم الداخلي في Joryio، id سداسي من 24 حرفاً في استجابات Users APIبديل لـ userId عندما تريد الإشارة بمعرّف Joryio

تعريف مرن للمستخدم​

عند إنشاء الطلبات أو تتبع الأحداث، يمكنك تعريف المستخدمين بـ:

  • userId - معرّف المستخدم لديك، وهو الأكثر شيوعاً.
  • joryioUserId - معرّف المستخدم الداخلي في Joryio.
// Using your user ID (recommended)
{ "orderId": "ORD-001", "userId": "user-123", ... }

// Using Joryio's internal user ID
{ "orderId": "ORD-001", "joryioUserId": "66a1f2c3d4e5f6a7b8c9d0e1", ... }

المصادقة​

تتطلب كل الطلبات مصادقة JWT:

Authorization: Bearer your_jwt_token
Content-Type: application/json
X-Workspace-Id: your_workspace_id

كتالوج المنتجات​

إنشاء منتج​

أنشئ منتجاً جديداً في كتالوجك.

POST /catalog/products

نص الطلب:

الحقلالنوعمطلوبالوصف
productIdstringنعممعرّف منتجك الفريد، SKU أو معرّف المنتج
namestringنعماسم المنتج
pricenumberنعمسعر المنتج
descriptionstringلاوصف المنتج
compareAtPricenumberلاالسعر الأصلي للخصومات
currencystringلارمز العملة، الافتراضي USD
categoriesstring[]لافئات المنتج
tagsstring[]لاوسوم المنتج
brandstringلااسم العلامة التجارية
imageUrlstringلاURL لصورة المنتج الرئيسية
urlstringلاURL لصفحة المنتج
inStockbooleanلاتوافر المخزون، الافتراضي true
skustringلاوحدة حفظ المخزون
variantsobject[]لامتغيرات المنتج
customFieldsobjectلاسمات مخصصة

مثال لطلب:

curl -X POST https://api-eu1.joryio.com/api/catalog/products \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"productId": "SKU-12345",
"name": "Classic Blue T-Shirt",
"price": 29.99,
"compareAtPrice": 39.99,
"currency": "USD",
"categories": ["Clothing", "T-Shirts"],
"tags": ["sale", "bestseller"],
"brand": "Acme Apparel",
"imageUrl": "https://example.com/images/blue-tshirt.jpg",
"url": "https://example.com/products/blue-tshirt",
"inStock": true,
"sku": "BTS-001-BL",
"variants": [
{
"id": "var-s",
"name": "Small",
"sku": "BTS-001-BL-S",
"price": 29.99,
"inStock": true,
"options": { "size": "S", "color": "Blue" }
}
]
}'

الاستجابة:

{
"id": "prod_abc123",
"productId": "SKU-12345",
"name": "Classic Blue T-Shirt",
"price": 29.99,
"createdAt": "2024-01-20T10:00:00.000Z"
}

Upsert جماعي للمنتجات، نص مصفوفة​

لا توجد نقطة نهاية جماعية منفصلة: يقبل POST /catalog/products كائن منتج واحداً أو مصفوفة JSON مجردة من المنتجات، بلا كائن غلاف. ينشئ شكل المصفوفة أو يحدّث حتى 500 منتج في الطلب الواحد، وفق productId.

POST /catalog/products

المصفوفة بحد أقصى 500 عنصر؛ تعيد المصفوفة الفارغة أو ما يزيد على 500 رمز 400. يتبع كل عنصر تنسيق كائن المنتج الواحد، بما فيه الحقل الاختياري source لكل منتج.

يتحقق النظام من كل عنصر بالكامل. يُبلغ عن العنصر غير الصالح في failed بفهرسه ولا يُقبل بصمت، بينما تستمر عملية upsert للعناصر الصالحة.

[
{ "productId": "SKU-001", "name": "Product 1", "price": 19.99 },
{ "productId": "SKU-002", "name": "Product 2", "price": 29.99, "source": "shopify" }
]

الاستجابة:

{
"processed": 2,
"upserted": 2,
"failed": []
}
الحقلالوصف
processedعدد العناصر التي استقبلتها مصفوفة الطلب
upsertedالمنتجات التي أُنشئت أو حُدثت فعلياً
failedفشل لكل عنصر: index وproductId أو sku إن وُجد وreason

عرض المنتجات​

استعلم عن المنتجات بالتصفية والترقيم.

GET /catalog/products
المعاملالنوعالوصف
searchstringبحث بالاسم أو الوصف أو SKU
categoriesstring[]تصفية بالفئات
tagsstring[]تصفية بالوسوم
brandstringتصفية بالعلامة
inStockbooleanتصفية بحالة المخزون
minPricenumberأقل سعر
maxPricenumberأعلى سعر
limitnumberنتائج الصفحة، الافتراضي 50
offsetnumberإزاحة الترقيم
sortBystringحقل الترتيب: name أو price أو createdAt أو updatedAt
sortOrderstringasc أو desc
curl "https://api-eu1.joryio.com/api/catalog/products?categories=T-Shirts&inStock=true&limit=20" \
-H "Authorization: Bearer $TOKEN"

حقول تملؤها تكاملات المتاجر. تتضمن استجابات المنتج أيضًا حقولًا يملؤها متجر متصل (WooCommerce) ولا تقبلها مسارات الإنشاء والتحديث: gtin (GTIN أو UPC أو EAN أو ISBN، أو null)، وattributes (اسم السمة إلى قيمها، مثل {"Material": ["Linen"]}، أو null)، وvariants[].gtin، وsourceModifiedAt (آخر وقت غيّر فيه المتجر المنتج). ويُملأ brand وdescription (نص عادي) بالطريقة نفسها للمنتجات المتزامنة.

الحصول على الفئات​

اعرض جميع فئات المنتجات.

GET /catalog/categories

الحصول على العلامات​

اعرض جميع علامات المنتجات.

GET /catalog/brands

الطلبات​

إنشاء طلب​

تتبّع طلباً جديداً.

POST /orders

نص الطلب:

الحقلالنوعمطلوبالوصف
orderIdstringنعممعرّف طلبك أو رقمه في منصتك
userIdstringأحدهمامعرّف مستخدم العميل لديك
joryioUserIdstringأحدهمامعرّف المستخدم الداخلي في Joryio، بديل لـ userId
totalnumberنعمإجمالي الطلب
itemsobject[]نعمبنود الطلب
statusstringلاحالة الطلب، الافتراضي pending
paymentStatusstringلاpaid أو unpaid: هل دُفع الطلب. طلب الدفع عند الاستلام أو التحويل البنكي يكون processing وunpaid حتى يصل المال، ويظهر في صفحة الطلبات بانتظار الدفع بدلًا من مدفوع. ومن دونه يُحتسب الطلب قيد المعالجة / المشحون / المُسلّم مدفوعًا (ويحتفظ الطلب الموجود بقيمته). لا تعتمد الإيرادات عليه. راجع مدفوع أو بانتظار الدفع.
currencystringلارمز العملة
subtotalnumberلاالمجموع قبل الخصومات
discountnumberلامقدار الخصم
shippingnumberلاتكلفة الشحن
taxnumberلامقدار الضريبة
totalRefundednumberلاالمبلغ المسترد حتى الآن بعملة الطلب، الافتراضي 0. في الاسترداد الجزئي أرسل المبلغ الجزئي مع status: partiallyRefunded؛ وللكامل عيّنه إلى total مع status: refunded. تُخصم الاستردادات من الإيراد المنسوب.
refundsobject[]لاكل الاستردادات حتى الآن (حتى 100): { "id": "RF-1", "amount": 29.99, "createdAt": "2026-06-03T09:00:00Z", "reason": "Wrong size", "items": [{ "productId": "SKU-1", "quantity": 1 }] } - الحقل amount وحده إلزامي. كل استرداد جديد يُطلق حدث Order Refunded واحدًا (حسب id؛ ومن دونه، حسب ارتفاع المبلغ المسترد)، والاسترداد الذي أُبلغ عنه سابقًا لا يُطلق شيئًا مرة أخرى. إذا أُرسلت دون totalRefunded فمجموعها هو المبلغ المسترد. الانتقال إلى status: cancelled (أو POST /orders/:id/cancel) يُطلق حدث Order Cancelled واحدًا. راجع أحداث الاسترداد والإلغاء.
couponCodestringلاالقسيمة المطبقة
couponsobject[]لاكل رموز القسائم المستخدمة في الطلب (حتى 50): { "code": "SUMMER10", "amount": 10.0, "type": "fixed_amount" }. الحقلان amount (الخصم الذي منحه الرمز) وtype اختياريان. في upsert يستبدل coupons الرموز المحفوظة ([] يمسحها)؛ احذفه للإبقاء عليها. عند إرسال الاثنين، تكون الأولوية لـcoupons على couponCode
shippingAddressobjectلاعنوان الشحن
campaignIdstringلاCampaign الإسناد
canvasIdstringلاCanvas الإسناد
sourcestringلامصدر الطلب، مثل email أو sms أو direct
utmSourcestringلامصدر UTM
utmMediumstringلاوسيط UTM
utmCampaignstringلاCampaign UTM
salesChannelstringلامكان إجراء الطلب: online أو pos أو app أو phone أو marketplace أو other (بأي حالة أحرف). أي قيمة أخرى تعيد 400. عند حذفه يكون الطلب الجديد online ويحتفظ الطلب الموجود بقناته. انظر المتجر وقناة البيع
storeIdstringلامعرّفك الخاص للمتجر الذي ينتمي إليه الطلب (متجر فعلي، فرع، موقع ثانٍ)، حتى 100 حرف. يطابقه فلتر المتجر في صفحة الطلبات وشرط الشريحة "اشترى في المتجر"
storeNamestringلااسم العرض للمتجر، حتى 255 حرفًا، يظهر مع storeId
تعريف المستخدم

يلزم تقديم userId أو joryioUserId. استخدم userId مع معرّفات مستخدميك، وهو الأكثر شيوعاً. واستخدم joryioUserId عندما تملك id الداخلي لـ Joryio من استجابات Users API.

بنية بند الطلب:

{
"productId": "prod_abc123",
"name": "Blue T-Shirt",
"sku": "BTS-001",
"quantity": 2,
"price": 29.99,
"total": 59.98,
"imageUrl": "https://example.com/image.jpg"
}

مثال لطلب:

curl -X POST https://api-eu1.joryio.com/api/orders \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"orderId": "ORD-2024-001",
"userId": "user-123",
"total": 89.97,
"subtotal": 99.97,
"discount": 10.00,
"shipping": 5.99,
"tax": 4.99,
"currency": "USD",
"coupons": [
{ "code": "SAVE10", "amount": 10.00, "type": "fixed_amount" }
],
"items": [
{ "productId": "prod_abc123", "name": "Blue T-Shirt", "quantity": 2, "price": 29.99, "total": 59.98 }
],
"source": "email",
"salesChannel": "pos",
"storeId": "tlv-dizengoff",
"storeName": "Tel Aviv - Dizengoff",
"campaignId": "camp_xyz789"
}'

المتجر وقناة البيع​

يحمل كل طلب مكان بيعه (salesChannel)، وعند تحديده، المتجر الذي ينتمي إليه (storeId). وبذلك يستطيع تاجر لديه متجر فعلي وموقع، أو متجران، تصفية الطلبات وتقسيمها حسب كل منهما وبناء شرائح مثل "اشترى في المتجر الفعلي خلال آخر 90 يومًا".

salesChannelالمعنى
onlineموقعك الإلكتروني أو الدفع عبر الويب (الافتراضي)
posنقطة بيع في متجر فعلي
appتطبيقك للهاتف المحمول
phoneأدخله الموظفون: هاتف أو بريد إلكتروني أو طلب يدوي أو مسودة
marketplaceسوق إلكتروني تابع لجهة خارجية
otherأي شيء آخر

القيم ثابتة ومتطابقة في كل مكان: في هذه الواجهة، وفي خصائص الأحداث (sales_channel)، وفي شروط الشرائح، وفي تصدير CSV للطلبات.

المتاجر المتصلة تملأ الحقلين نيابةً عنك. المتجر هو اتصال المتجر نفسه، والقناة مأخوذة مما تسجله المنصة:

المتجرقناة البيع
Shopifyمن source_name للطلب: web وonline_store وcheckout وطلبات الاشتراك هي online؛ وpos هو pos؛ وshopify_draft_order وiphone وandroid (طلبات ينشئها الموظفون في لوحة الإدارة أو تطبيق Shopify) هي phone؛ وأي شيء آخر other
WooCommerceمن created_via: checkout وstore-api هما online؛ وadmin هو phone؛ وإضافة نقطة بيع هي pos؛ وrest-api وأي شيء آخر other
Magentoالطلب المنشأ في لوحة الإدارة (بلا عنوان IP للمشتري) هو phone؛ وكل طلب آخر online
PrestaShopدائمًا online - لا تسجل PrestaShop مكان إجراء الطلب

يمكن لحدث Order Completed من SDK إرسال sales_channel (وstore_id وstore_name). وبدونه يكون الطلب من تطبيق iOS أو Android هو app والطلب من موقع ويب online. تحمل أحداث Order Completed وOrder Refunded وOrder Cancelled الخاصية sales_channel، وstore_id عندما يكون للطلب متجر، ليتمكن المسار من البدء بناءً عليها.

تنفيذ الطلب​

علِّم الطلب بأنه شُحن.

POST /orders/:id/fulfill
{
"trackingNumber": "1Z999AA10123456784",
"carrier": "UPS"
}

إلغاء الطلب​

POST /orders/:id/cancel
{
"reason": "Customer requested cancellation"
}

استرداد الطلب​

POST /orders/:id/refund
{
"refundAmount": 29.99,
"reason": "Product defective",
"partial": true
}

إحصاءات الطلبات​

احصل على إحصاءات الطلبات لنطاق تاريخ.

GET /orders/stats?startDate=2024-01-01&endDate=2024-01-31
{
"totalOrders": 156,
"totalRevenue": 12450.50,
"averageOrderValue": 79.81,
"ordersByStatus": {
"pending": 5,
"processing": 12,
"shipped": 45,
"delivered": 90,
"cancelled": 4
}
}

تتبع السلة​

تحديث سلة​

تتبّع سلة مستخدم أو حدّثها.

PUT /carts
{
"userId": "user_123",
"items": [
{
"productId": "prod_abc123",
"name": "Blue T-Shirt",
"price": 29.99,
"quantity": 2,
"total": 59.98,
"imageUrl": "https://example.com/image.jpg"
}
],
"value": 59.98,
"subtotal": 59.98,
"currency": "USD",
"checkoutUrl": "https://store.example.com/checkout?cart=abc123"
}

value هو إجمالي السلة كما تحسبه (قد يشمل الشحن والضريبة). وsubtotal اختياري: المجموع الفرعي للعناصر، قبل الشحن والضريبة - وإذا لم يُرسل يُحسب من العناصر (total لكل سطر، وإلا price x quantity). والمجموع الفرعي هو قيمة السلة في التجارة الإلكترونية ← السلال، وفي فلتر الشريحة قيمة السلة، وفي minValue وtotalAbandonedValue.

الإضافة إلى السلة​

أضف عنصراً إلى سلة مستخدم.

POST /carts/add
{
"userId": "user_123",
"item": {
"productId": "prod_abc123",
"name": "Blue T-Shirt",
"price": 29.99,
"quantity": 1,
"total": 29.99
}
}

الحصول على السلات المتروكة​

اعرض السلات المتروكة لحملات الاستعادة.

GET /carts/abandoned
المعاملالنوعالوصف
minValuenumberأقل قيمة للسلة
abandonedMinutesAgonumberأقصى دقائق منذ التخلي
limitnumberالنتائج في الصفحة
offsetnumberإزاحة الترقيم
{
"data": [
{
"id": "cart_123",
"userId": "user_456",
"items": [...],
"value": 149.99,
"abandonedAt": "2024-01-20T15:30:00.000Z",
"checkoutUrl": "https://store.example.com/checkout?cart=abc"
}
],
"total": 42,
"hasMore": true
}

إحصاءات السلات​

GET /carts/stats
{
"totalCarts": 1250,
"abandonedCarts": 312,
"recoveredCarts": 87,
"totalAbandonedValue": 45670.50,
"recoveryRate": 27.88
}

تحليل RFM​

يقسم تحليل RFM، الحداثة والتكرار والقيمة النقدية، العملاء بحسب سلوك الشراء.

الحصول على بيانات RFM لمستخدم​

احصل على درجات RFM والشريحة لمستخدم محدد.

GET /rfm/user/:userId

:userId هو معرّف المستخدم الداخلي أو المعرّف الخارجي الذي يتعرف به تطبيقكم على الشخص؛ يعيد المعرّف غير المعروف 404.

{
"totalOrders": 12,
"totalSpent": 849.50,
"averageOrderValue": 70.79,
"firstOrderDate": "2023-06-15T10:00:00.000Z",
"lastOrderDate": "2024-01-18T14:30:00.000Z",
"daysSinceLastOrder": 2,
"hasActiveCart": false,
"rfmRecency": 5,
"rfmFrequency": 4,
"rfmMonetary": 4,
"rfmScore": "544",
"rfmSegment": "champions"
}

حقول RFM تكون null لجهة اتصال ليس لها طلب محسوب (الطلبات الملغاة والمستردة لا تُحتسب).

الحصول على توزيع RFM​

احصل على توزيع العملاء في شرائح RFM.

GET /rfm/distribution
{
"champions": { "count": 150, "totalValue": 125000 },
"loyal": { "count": 320, "totalValue": 89000 },
"potential_loyalists": { "count": 180, "totalValue": 32000 },
"new_customers": { "count": 450, "totalValue": 28000 },
"at_risk": { "count": 95, "totalValue": 42000 },
"lost": { "count": 280, "totalValue": 15000 },
"scoring": {
"computedAt": "2026-09-02T02:00:00.000Z",
"method": "quantile",
"buyers": 1475
}
}

المستويات هي أخماس داخل المتجر (method: "quantile"): يُرتَّب كل من الحداثة والتكرار وصافي الإنفاق عبر مشتري المتجر ويُقسَّم إلى خمس مجموعات متساوية، فالمستوى الأعلى يعني الخُمس الأعلى من عملائك أنت، بأي عملة. المتجر الذي يقل عدد مشتريه عن 25 يستخدم حدودًا ثابتة (method: "fixed"). يُعاد حساب الدرجات كل ليلة ومع كل طلب؛ ويحدد scoring.computedAt متى.

شرائح RFM​

الشريحةالوصفدرجات RFM المعتادة
Championsأفضل العملاء، يشترون كثيراً وينفقون أكثر555، 554، 545
Loyalعملاء متسقون444، 443، 434
Potential Loyalistsحديثو العهد بتكرار متوسط433، 343، 333
New Customersأجروا أول شراء للتو511، 512، 411
Promisingحديثو العهد لكن بتكرار منخفض422، 322، 312
Needs Attentionتفاعل متوسط آخذ في الانخفاض332، 322، 233
About to Sleepدون المتوسط ومعرّضون للخطر211، 212، 221
At Riskكانوا أوفياء ولم يشتروا حديثاً144، 143، 244
Can't Loseكانوا أفضل العملاء وهم الآن غير نشطين155، 154، 255
Hibernatingتفاعل منخفض وعدم نشاط طويل122، 121، 112
Lostأدنى الدرجات ومن المرجح فقدانهم111

التتبع في متجر Shopify​

يفعّل (أو يحدّث) بكسل Joryio في متجر Shopify المتصل - أحداث المتجر وصفحة الدفع - ويكتب الإعدادات التي يقرأها app embed الخاص بـ Joryio. وعندما يصبح البكسل نشطًا يتوقف سكربت التتبع القديم في المتجر (المتاجر المرتبطة قبل أكتوبر 2026) عن إرسال الأحداث - لا يحذفه Joryio؛ يزيله Shopify عند إلغاء تثبيت التطبيق ويتوقف عن تحميله في 1 مارس 2027. يمكن استدعاؤه مرة أخرى بأمان. يتطلب integrations:write.

POST /api/integrations/shopify/storefront-tracking
Authorization: Bearer YOUR_API_KEY
{
"pixel": { "status": "active", "id": "gid://shopify/WebPixel/123", "error": null, "checkedAt": "2026-10-03T10:00:00.000Z" },
"missingScopes": [],
"legacyScriptTag": false,
"appEmbed": {
"configured": true,
"themeEditorUrl": "https://my-store.myshopify.com/admin/themes/current/editor?context=apps&activateAppId=<api-key>/joryio-embed"
}
}
pixel.statusالمعنى
activeالبكسل مثبت في المتجر ويرسل الأحداث
needs_scopesلم يوافق المتجر على write_pixels وread_customer_events. ابدأ ربط Shopify من جديد (سيُطلب من صاحب المتجر الموافقة عليها)؛ وتسردها missingScopes
errorرفض Shopify البكسل؛ ويوضح pixel.error السبب
not_registeredلم يتم الإعداد بعد

يعيد GET /api/integrations/shopify الكائن نفسه في الحقل storefrontTracking. راجع تكامل Shopify.

ويعيد أيضًا themeEmbed، وهو صف النوافذ المنبثقة في متجرك: يفتح activateUrl محرر القالب مع تفعيل embed الخاص بـ Joryio (يضغط صاحب المتجر حفظ؛ وتكون القيمة null عندما لا يكون للخادم تطبيق Shopify مُعد)، وتكون status هي on عندما يبلّغ SDK المتجر خلال آخر 7 أيام، وإلا not_reported. راجع تفعيل النوافذ المنبثقة.

"themeEmbed": {
"activateUrl": "https://my-store.myshopify.com/admin/themes/current/editor?context=apps&activateAppId=<api-key>/joryio-embed",
"status": "not_reported"
}

installUrl (حتى عندما لا يكون هناك متجر متصل) هو العنوان الذي يرسل إليه تثبيت Joryio من Shopify التاجر الذي لم يثبّت Joryio بعد: صفحة تطبيق Joryio في Shopify App Store أو رابط تثبيت، أو null عندما لا يكون للخادم عنوان مُعد (عندها تربط لوحة التحكم بخطوات التثبيت). لا يطلب Joryio أبدًا عنوان المتجر - يُثبَّت المتجر من Shopify ثم يُربط. راجع ربط متجرك.

فتح التطبيق من Shopify​

عندما يثبّت تاجر تطبيق Joryio أو يفتحه من Shopify، يضيف Shopify إلى عنوان التطبيق معاملات موقّعة (shop وhmac وtimestamp وhost). أرسلها كما هي؛ يتحقق Joryio من توقيع Shopify ومن أن الرابط لا يتجاوز عمره ساعة، ويعيد حالة المتجر في مساحة العمل الحالية. لا يربط أي شيء أبدًا. يتطلب integrations:read.

POST /api/integrations/shopify/app-launch
Authorization: Bearer YOUR_API_KEY
Content-Type: application/json

{ "query": { "shop": "my-store.myshopify.com", "host": "YWRtaW4uc2hvcGlmeS5jb20vc3RvcmUvbXktc3RvcmU", "timestamp": "1760000000", "hmac": "<from Shopify>" } }
{ "status": "not_connected", "shopDomain": "my-store.myshopify.com" }
statusالمعنى
connected_hereالمتجر متصل بمساحة العمل هذه
connected_elsewhereمتصل بمساحة عمل أخرى في المؤسسة؛ يحددها workspaceId
not_connectedغير متصل في المؤسسة - ابدأ الربط عبر POST /api/integrations/shopify/oauth/initiate

المعاملات غير الموقّعة أو المعدّلة أو الأقدم من ساعة تحصل على 400. راجع تكامل Shopify.

إعداد إعادة الاشتراك من صفحة الدفع في المتجر​

يفعّل أو يعطّل إعداد السماح لموافقات الدفع بإعادة اشتراك من ألغوا اشتراكهم لاتصال المتجر. قيمة :platform هي shopify أو woocommerce أو magento أو prestashop. معطّل افتراضيًا. عند التفعيل، تعيد موافقة الدفع أو موافقة سجل العميل من هذا المتجر - بما في ذلك الدفع كضيف - المؤرخة بعد آخر إلغاء اشتراك لجهة الاتصال اشتراكها. لا يتجاوز أبدًا شكوى بريد مزعج أو ارتدادًا دائمًا أو STOP في SMS (فقط تأكيد اشتراك مزدوج) أو حظرًا على مستوى المؤسسة أو إلغاء اشتراك جهة اتصال أخرى. يُسجَّل كل تغيير في سجل التدقيق. يتطلب settings:write.

PUT /api/integrations/shopify/consent-settings
Authorization: Bearer YOUR_API_KEY
Content-Type: application/json

{ "checkoutResubscribe": true }
{ "checkoutResubscribe": true }

يعيد GET /api/integrations/<platform> القيمة الحالية في checkoutResubscribe. راجع موافقة التسويق من المتاجر.

ضمانات موافقة الدفع (WooCommerce وMagento وPrestaShop)​

يقبل المسار نفسه ضمانات Record consent immediately لاتصال WooCommerce أو Magento، والضمانات نفسها لمربع النشرة البريدية عند الدفع في PrestaShop، الذي يصل دائمًا إلى Joryio قبل الطلب (الوحدة 1.2.8 أو أحدث - راجع متى تُحتسب علامة الموافقة). قيمة :platform هي woocommerce أو magento أو prestashop؛ ويتلقى Shopify 400. التحديد عند الدفع الذي يصل قبل الطلب يُحتسب فورًا فقط لمشترٍ معروف أو مشترك؛ ويبقى أي شخص آخر معلّقًا حتى طلبه أو رابط التأكيد - راجع متى يُحتسب التحديد. يرسل checkoutConsentConfirmEmail (الافتراضي false) إلى هذا الضيف رابط تأكيد واحدًا بالبريد الإلكتروني. وcheckoutConsentTickLimit (1-100، الافتراضي 3) هو عدد التحديدات المقبولة لكل عنوان IP للمتسوق ولكل عملية دفع ولكل عنوان بريد إلكتروني في الساعة؛ ويُتجاهل أي تحديد يتجاوزه. أرسل أحدهما أو كليهما، وحدهما أو مع checkoutResubscribe. يُسجَّل كل تغيير في سجل التدقيق. يتطلب settings:write.

PUT /api/integrations/magento/consent-settings
Authorization: Bearer YOUR_API_KEY
Content-Type: application/json

{ "checkoutConsentConfirmEmail": true, "checkoutConsentTickLimit": 5 }
{ "checkoutConsentConfirmEmail": true, "checkoutConsentTickLimit": 5 }

تُرجع GET /api/integrations/woocommerce وGET /api/integrations/magento وGET /api/integrations/prestashop القيمتين.

newShopperOptIns - موافقات المتسوقين الجدد (الذين لم يشتروا بعد) - قيمته hold (الافتراضي) أو immediate. ويحكم كل موافقة تصل من دون طلب من شخص لا يعرفه Joryio بعد: تحديد عند الدفع أُرسل قبل الطلب، وموافقة عند التسجيل أو في صفحة الحساب أو في النشرة أو من مزامنة العملاء. يُبقيها hold معلّقة حتى الطلب أو رابط التأكيد؛ ويحتسبها immediate فورًا (أقل صرامة - يمكن لأي زائر إدخال أي بريد إلكتروني). يُحتسب المشترون والمشتركون الحاليون فورًا في الحالتين، ويُطبَّق إلغاء الاشتراك فورًا دائمًا، ويبقى checkoutConsentTickLimit ساريًا. المسار والمنصات نفسها، وحده أو مع الحقول الأخرى؛ ويُسجَّل كل تغيير في سجل التدقيق؛ ويتطلب settings:write. يعيده GET /api/integrations/<platform> باسم newShopperOptIns.

PUT /api/integrations/woocommerce/consent-settings
Authorization: Bearer YOUR_API_KEY
Content-Type: application/json

{ "newShopperOptIns": "immediate" }
{ "newShopperOptIns": "immediate" }

تُرجع GET /api/integrations/woocommerce وGET /api/integrations/magento أيضًا storeCheckoutOptIn: إعدادات الموافقة عند الدفع الخاصة بالإضافة نفسها كما أبلغت عنها آخر مرة (إضافة WooCommerce بالإصدار 1.6.16 أو أحدث، امتداد Magento بالإصدار 1.4.17 أو أحدث). وهي للقراءة فقط - تُضبط في المتجر، وليس عبر هذه الواجهة أبدًا - وقيمتها null حتى تُبلغ الإضافة عنها. قيمة recordMode هي order (فقط عند تقديم الطلب) أو immediate (تسجيل الموافقة فورًا).

{ "storeCheckoutOptIn": { "pluginVersion": "1.6.16", "checkoutBox": true, "preTicked": false, "recordMode": "order", "reportedAt": "2026-10-27T10:00:00.000Z" } }

هوية الزائر: التعرّف على الزوار من البريد الإلكتروني الذي يكتبونه​

Recognise visitors by the email they type - راجع WooCommerce وShopify. مفعّل افتراضيًا: البريد المكتوب عند الدفع أو في نموذج يعرّف متصفح الزائر على أنه جهة الاتصال صاحبة هذا البريد. معطّل (هوية صارمة): البريد المكتوب لا يعرّف المتصفح أبدًا؛ وتذهب السلال والطلبات وإرسالات النماذج مع ذلك إلى جهة اتصال البريد.

يعيد GET /api/integrations/<platform> الحقل visitorIdentity: identifyByTypedEmail وsource (default - لم يُبلَّغ عن شيء ولم يُضبط بعد، أي مفعّل؛ plugin - أبلغت عنه إضافة WooCommerce بالإصدار 1.6.22 أو أحدث، أو امتداد Magento بالإصدار 1.4.21 أو أحدث، أو وحدة PrestaShop بالإصدار 1.2.10 أو أحدث؛ joryio - ضُبط في Joryio) وpluginVersion وreportedAt. في WooCommerce وMagento وPrestaShop يوجد الإعداد في إضافة المتجر وهو للقراءة فقط هنا. في Shopify اضبطه بالمسار أدناه؛ وتحصل المنصات الأخرى على 400. يُسجَّل كل تغيير في سجل التدقيق. يتطلب settings:write.

PUT /api/integrations/shopify/visitor-identity
Authorization: Bearer YOUR_API_KEY
Content-Type: application/json

{ "identifyByTypedEmail": false }
{ "visitorIdentity": { "identifyByTypedEmail": false, "source": "joryio", "pluginVersion": null, "reportedAt": "2026-11-01T10:00:00.000Z" } }

اتصال PrestaShop (وصول مبكر)​

قراءة اتصال PrestaShop لمساحة العمل وإدارته (متجر PrestaShop واحد لكل اتصال؛ راجعوا تكامل PrestaShop). يعيد GET القيمة { "connected": false, "platform": "prestashop" } عند عدم وجود اتصال. يعيد المسار api-key مفتاح jry_ps_ الذي تلصقونه في الوحدة (يتطلب integrations:write، مثل settings وsync وDELETE). يعيد GET أيضًا workspaceId وprestashopVersion وmoduleVersion. يقبل sync القيمة type: full (الافتراضي) أو delta (المنتجات والعملاء والطلبات التي تغيرت منذ آخر مزامنة ناجحة) أو products أو customers أو orders أو carts (السلات المفتوحة خلال آخر 24 ساعة)، ويعود فورًا بينما تعمل المزامنة. يقطع DELETE اتصال المتجر ويلغي المفتاح.

GET /api/integrations/prestashop
Authorization: Bearer YOUR_API_KEY
{
"connected": true,
"status": "connected",
"platform": "prestashop",
"storeName": "My Shop",
"storeUrl": "https://shop.example.com",
"shopId": 1,
"workspaceId": "6f1c2d3e-...",
"prestashopVersion": "8.2.1",
"moduleVersion": "1.0.0",
"syncSettings": { "syncProducts": true, "syncOrders": true, "syncCustomers": true, "syncCarts": true, "historicalDays": 90 },
"checkoutResubscribe": false,
"syncStats": { "productCount": 120, "orderCount": 860, "customerCount": 540 },
"lastSyncAt": "2026-10-21T10:00:00.000Z",
"connectedAt": "2026-10-21T09:58:00.000Z"
}

connectedAt هو آخر وقت اتصل فيه المتجر (تحدّثه كل إعادة اتصال)؛ وتُعيده أيضًا GET /api/integrations/shopify و/woocommerce و/magento. للاتصال الذي سبق تسجيل هذا الحقل يُعاد وقت إنشاء السجل.

GET /api/integrations/prestashop/api-key
Authorization: Bearer YOUR_API_KEY
{ "apiKey": "jry_ps_...", "isNew": false }
PUT /api/integrations/prestashop/settings
Authorization: Bearer YOUR_API_KEY
Content-Type: application/json

{ "syncCarts": true, "historicalDays": 90 }
POST /api/integrations/prestashop/sync
Authorization: Bearer YOUR_API_KEY
Content-Type: application/json

{ "type": "full" }
DELETE /api/integrations/prestashop
Authorization: Bearer YOUR_API_KEY

Webhooks​

تطلق أحداث التجارة الإلكترونية webhooks يمكن أن تشغّل مسارات Canvas:

الحدثالوصف
ecommerce.order.createdإنشاء طلب جديد
ecommerce.order.fulfilledشحن الطلب
ecommerce.order.cancelledإلغاء الطلب
ecommerce.order.refundedاسترداد الطلب
ecommerce.order.status_changedتغير حالة الطلب
ecommerce.cart.abandonedتعليم السلة كمتروكة
ecommerce.cart.recoveredاستعادة سلة متروكة
ecommerce.cart.updatedتغير محتويات السلة

البحث بمعرّفاتك​

الحصول على منتج بمعرّف منتجك​

استرجع منتجاً باستخدام معرّفك:

GET /catalog/products/by-product-id/:productId

المعرّف الذي لا يقابله سجل يعيد 404، وليس 200 بجسم فارغ.

curl "https://api-eu1.joryio.com/api/catalog/products/by-product-id/SKU-12345" \
-H "Authorization: Bearer $TOKEN"

الحصول على طلب بمعرّف طلبك​

استرجع طلباً باستخدام معرّفك:

GET /orders/by-order-id/:orderId

المعرّف الذي لا يقابله سجل يعيد 404، وليس 200 بجسم فارغ.

curl "https://api-eu1.joryio.com/api/orders/by-order-id/ORD-2024-001" \
-H "Authorization: Bearer $TOKEN"

الطلبات والسلال وRFM عبر الـAPI​

إدراج/تحديث الطلبات دفعةً واحدة​

POST /orders/upsert

الطريقة الأساسية التي يرسل بها متجر ليس على Shopify أو WooCommerce أو Magento طلباته. عملية عديمة الأثر التكراري حسب orderId: إعادة إرسال الطلب نفسه تحدّثه بدل إنشاء نسخة مكررة، فإعادة المحاولة بعد فشل الشبكة آمنة.

أرسل كل طلب بالعملة التي دُفع بها دون أي تحويل: يحوّل Joryio قيمة total إلى عملة التقارير لمساحة العمل بسعر البنك المركزي الأوروبي في تاريخ placedAt للطلب، وتجمع التقارير المبلغ المحوَّل.

نص الطلب:

{
"orders": [
{
"orderId": "1001",
"userId": "user_123",
"email": "[email protected]",
"total": 89.90,
"currency": "EUR",
"placedAt": "2026-08-27T10:15:00Z",
"status": "completed",
"items": [
{ "productId": "prod_abc123", "quantity": 2, "price": 29.95 }
]
}
]
}

الاستجابة:

{ "upserted": 1, "created": 1, "updated": 0 }

اتجاه الإيرادات​

GET /orders/revenue-trend?days=30&granularity=day

صافي الإيراد عبر الزمن (بعد استبعاد المستردات والإلغاءات). القيمة granularity هي day أو week أو month.

الاستجابة:

{
"currency": "EUR",
"points": [
{ "date": "2026-08-25", "revenue": 1240.50, "orders": 14 },
{ "date": "2026-08-26", "revenue": 980.00, "orders": 11 }
]
}

دورة حياة السلة​

POST /carts/remove
POST /carts/clear
POST /carts/mark-abandoned
POST /carts/recovered

أربع عمليات تقود رحلات استرداد السلة. كلٌّ من clear وrecovered ينهي السلة وهما غير متكافئين: recovered يقول إن الاسترداد نجح وينسب الطلب إليه، بينما clear يقول إن السلة زالت فحسب. استخدام clear بعد الشراء يمحو بصمت الدليل على أن رحلة الاسترداد حققت شيئًا.

{ "userId": "user_123", "orderId": "1001" }

سرد شرائح RFM​

GET /rfm/segments

شرائح RFM في مساحة العمل مع أحجامها الحالية، كي تستهدفها الأنظمة المتكاملة دون إعادة حساب التقييم.

الاستجابة:

{
"segments": [
{ "segment": "champions", "users": 412, "avgRecencyDays": 8, "avgOrders": 6.2 },
{ "segment": "at_risk", "users": 1180, "avgRecencyDays": 96, "avgOrders": 2.1 }
],
"computedAt": "2026-08-27T02:10:00Z"
}

إحصاءات الكتالوج​

GET /catalog/stats

الإجماليات وسلامة الكتالوج، محسوبة على كل المنتجات - لا على صفحة واحدة.

الاستجابة:

{
"productCount": 8214,
"categoryCount": 37,
"brandCount": 12,
"outOfStockCount": 341,
"lowStockCount": 96,
"unclassifiedCount": 18,
"lowStockThreshold": 10
}

يحصي outOfStockCount المنتجات ذات الكمية 0 أو inStock: false حين لا تُتتبَّع كمية؛ وlowStockCount ما بين 1 وlowStockThreshold؛ وunclassifiedCount المنتجات بلا فئة أو علامة تجارية.

استجابات الأخطاء​

{
"statusCode": 404,
"message": "Product SKU-12345 not found",
"error": "Not Found"
}
رمز الحالةالوصف
400نص طلب غير صالح
401المصادقة مطلوبة
403صلاحيات غير كافية
404المورد غير موجود
409تعارض، مثل معرّف خارجي مكرر
500خطأ داخلي في الخادم