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
نص الطلب:
| الحقل | النوع | مطلوب | الوصف |
|---|---|---|---|
productId | string | نعم | معرّف منتجك الفريد، SKU أو معرّف المنتج |
name | string | نعم | اسم المنتج |
price | number | نعم | سعر المنتج |
description | string | لا | وصف المنتج |
compareAtPrice | number | لا | السعر الأصلي للخصومات |
currency | string | لا | رمز العملة، الافتراضي USD |
categories | string[] | لا | فئات المنتج |
tags | string[] | لا | وسوم المنتج |
brand | string | لا | اسم العلامة التجارية |
imageUrl | string | لا | URL لصورة المنتج الرئيسية |
url | string | لا | URL لصفحة المنتج |
inStock | boolean | لا | توافر المخزون، الافتراضي true |
sku | string | لا | وحدة حفظ المخزون |
variants | object[] | لا | متغيرات المنتج |
customFields | object | لا | سمات مخصصة |
مثال لطلب:
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
| المعامل | النوع | الوصف |
|---|---|---|
search | string | بحث بالاسم أو الوصف أو SKU |
categories | string[] | تصفية بالفئات |
tags | string[] | تصفية بالوسوم |
brand | string | تصفية بالعلامة |
inStock | boolean | تصفية بحالة المخزون |
minPrice | number | أقل سعر |
maxPrice | number | أعلى سعر |
limit | number | نتائج الصفحة، الافتراضي 50 |
offset | number | إزاحة الترقيم |
sortBy | string | حقل الترتيب: name أو price أو createdAt أو updatedAt |
sortOrder | string | asc أو 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
نص الطلب:
| الحقل | النوع | مطلوب | الوصف |
|---|---|---|---|
orderId | string | نعم | معرّف طلبك أو رقمه في منصتك |
userId | string | أحدهما | معرّف مستخدم العميل لديك |
joryioUserId | string | أحدهما | معرّف المستخدم الداخلي في Joryio، بديل لـ userId |
total | number | نعم | إجمالي الطلب |
items | object[] | نعم | بنود الطلب |
status | string | لا | حالة الطلب، الافتراضي pending |
paymentStatus | string | لا | paid أو unpaid: هل دُفع الطلب. طلب الدفع عند الاستلام أو التحويل البنكي يكون processing وunpaid حتى يصل المال، ويظهر في صفحة الطلبات بانتظار الدفع بدلًا من مدفوع. ومن دونه يُحتسب الطلب قيد المعالجة / المشحون / المُسلّم مدفوعًا (ويحتفظ الطلب الموجود بقيمته). لا تعتمد الإيرادات عليه. راجع مدفوع أو بانتظار الدفع. |
currency | string | لا | رمز العملة |
subtotal | number | لا | المجموع قبل الخصومات |
discount | number | لا | مقدار الخصم |
shipping | number | لا | تكلفة الشحن |
tax | number | لا | مقدار الضريبة |
totalRefunded | number | لا | المبلغ المسترد حتى الآن بعملة الطلب، الافتراضي 0. في الاسترداد الجزئي أرسل المبلغ الجزئي مع status: partiallyRefunded؛ وللكامل عيّنه إلى total مع status: refunded. تُخصم الاستردادات من الإيراد المنسوب. |
refunds | object[] | لا | كل الاستردادات حتى الآن (حتى 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 واحدًا. راجع أحداث الاسترداد والإلغاء. |
couponCode | string | لا | القسيمة المطبقة |
coupons | object[] | لا | كل رموز القسائم المستخدمة في الطلب (حتى 50): { "code": "SUMMER10", "amount": 10.0, "type": "fixed_amount" }. الحقلان amount (الخصم الذي منحه الرمز) وtype اختياريان. في upsert يستبدل coupons الرموز المحفوظة ([] يمسحها)؛ احذفه للإبقاء عليها. عند إرسال الاثنين، تكون الأولوية لـcoupons على couponCode |
shippingAddress | object | لا | عنوان الشحن |
campaignId | string | لا | Campaign الإسناد |
canvasId | string | لا | Canvas الإسناد |
source | string | لا | مصدر الطلب، مثل email أو sms أو direct |
utmSource | string | لا | مصدر UTM |
utmMedium | string | لا | وسيط UTM |
utmCampaign | string | لا | Campaign UTM |
salesChannel | string | لا | مكان إجراء الطلب: online أو pos أو app أو phone أو marketplace أو other (بأي حالة أحرف). أي قيمة أخرى تعيد 400. عند حذفه يكون الطلب الجديد online ويحتفظ الطلب الموجود بقناته. انظر المتجر وقناة البيع |
storeId | string | لا | معرّفك الخاص للمتجر الذي ينتمي إليه الطلب (متجر فعلي، فرع، موقع ثانٍ)، حتى 100 حرف. يطابقه فلتر المتجر في صفحة الطلبات وشرط الشريحة "اشترى في المتجر" |
storeName | string | لا | اسم العرض للمتجر، حتى 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
| المعامل | النوع | الوصف |
|---|---|---|
minValue | number | أقل قيمة للسلة |
abandonedMinutesAgo | number | أقصى دقائق منذ التخلي |
limit | number | النتائج في الصفحة |
offset | number | إزاحة الترقيم |
{
"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",
"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 | خطأ داخلي في الخادم |