واجهة REST
واجهة REST
المسار الأساسي /api/v1. صادق بمفتاح API للمتجر (wjh_…). كل الاستجابات بنفس الغلاف: status و message و data.
العنوان الأساسي والغلاف
استدعِ الواجهة على مضيف API وجهة تحت /api/v1. الاستجابات الناجحة والأخطاء بنفس الشكل:
{
"status": 200,
"message": "ok",
"data": { }
}رمز HTTP يطابق حقل status (مثل 201 للإنشاء، و401/403/429 للمصادقة وحدود المعدل).
المصادقة
أنشئ مفتاح API من بوابة التاجر → إعدادات المتجر → المطورون → مفاتيح API. أرسله بأحد الترويسات:
Authorization: Bearer wjh_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# or
x-api-key: wjh_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxيُعرض المفتاح مرة واحدة عند الإنشاء. البادئة دائمًا wjh_.
حدود المعدل
تُطبَّق الحدود لكل مفتاح API مصادق (وحدّ IP منفصل قبل المصادقة):
- ١٢٠ طلبًا في الدقيقة لكل مفتاح API
- ٣٠٠ طلب في الدقيقة لكل IP (قبل المصادقة)
{
"status": 429,
"message": "Too many requests",
"data": null,
"code": "RATE_LIMITED"
}النطاقات
يُمنح كل مفتاح نطاقًا واحدًا أو أكثر. النطاق الناقص → 403.
- orders.crud — إنشاء وقراءة وتحديث وإلغاء الطلبات
- products.crud — إنشاء وقراءة وتحديث وحذف المنتجات
- collections.crud — إنشاء وقراءة وتحديث وحذف المجموعات
- expenses.crud — إنشاء وقراءة وتحديث وحذف المصروفات
- store.read — قراءة ملف المتجر
- customers.read — قراءة العملاء (customers.crud يمنح القراءة أيضًا)
- analytics.read — قراءة مركز التحليلات
- discounts.read — قراءة أكواد الخصم
- reviews.read — قراءة تقييمات المنتجات
- shipping_fees.read — قراءة رسوم شحن العميل حسب المنطقة
- balance.read — قراءة رصيد الشحن / المحفظة
- webhooks.read — عرض نقاط الويب هوك والأحداث
- webhooks.write — إنشاء نقاط الويب هوك
مرجع القيم (Enums)
قيم نصية تُرجعها وتقبلها /api/v1. استخدم القيم كما هي (حساسة لحالة الأحرف).
حالة الطلب (order.status)
| القيمة | المعنى |
|---|---|
| pending | طلب جديد — لم يجهز للاستلام / الحجز بعد |
| waiting_for_pickup | في انتظار استلام المندوب |
| ready | جاهز — حُجز المخزون / جرت محاولة الحجز حسب الإعداد |
| packed | تم التغليف وبانتظار الشحن |
| shipped | سُلِّم لشركة الشحن / قيد النقل |
| delivered | تم التسليم للعميل |
| returned | مرتجع بعد الشحن |
| cancelled | ملغي (DELETE /orders/:id ينتقل هنا عند السماح) |
قناة الطلب (order.channel)
| القيمة | المعنى |
|---|---|
| manual | أُنشئ خارج الأتمتة (API / تشغيل) |
| portal | أُنشئ من بوابة التاجر |
| storefront | أُنشئ من متجر الواجهة |
| shopify | مزامن من Shopify عند الربط |
طريقة الدفع (order.payment.method)
| القيمة | المعنى |
|---|---|
| cod | الدفع عند الاستلام — عادةً عيّن payment.codAmount |
| prepaid | مدفوع قبل الشحن (بطاقة / محفظة / رابط) |
| other | طرق غير شائعة |
حالة الشحنة (order.shipping.status)
| القيمة | المعنى |
|---|---|
| pending | سجل شحنة دون حجز بعد |
| created | محجوزة لدى شركة الشحن |
| in_transit | قيد النقل |
| out_for_delivery | خارج للتسليم |
| delivered | تم التسليم |
| returned | إرجاع للمنشأ |
| failed | فشل الحجز أو التسليم — راجع error / failureReason |
| cancelled | أُلغيت الشحنة |
توقيت التسليم (order.deliveryTiming)
| القيمة | المعنى |
|---|---|
| pending | لم يُقيَّم الوصول المتوقع بعد |
| on_time | ضمن نافذة الوصول المتوقع |
| early | أبكر من المتوقع |
| late | تجاوز الوصول المتوقع |
حالة المنتج / المجموعة
| القيمة | المعنى |
|---|---|
| active | نشط / ظاهر حسب الإعداد |
| draft | مسودة غير منشورة |
| archived | مؤرشف ومخفي (قد يؤرشف الحذف بدل المسح) |
حالة التقييم
| القيمة | المعنى |
|---|---|
| pending | بانتظار مراجعة التاجر |
| approved | ظاهر على المتجر |
| rejected | مرفوض من التاجر |
| hidden | مخفي من المتجر |
أحداث الويب هوك
| القيمة | المعنى |
|---|---|
| OrderCreated | طلب جديد |
| OrderUpdated | تغيّرت حقول الطلب |
| OrderStatusChanged | انتقال حالة الطلب |
| ProductUpdated | إنشاء/تحديث/حذف منتج |
| CollectionUpdated | إنشاء/تحديث/حذف مجموعة |
| CustomerCreated | عميل جديد |
| CustomerUpdated | تغيّرت حقول العميل |
| ShipmentCreated | حُجزت شحنة |
| ShipmentDelivered | وُسمت الشحنة كمُسلَّمة |
| ShipmentCreateFailed | فشل حجز الشحن |
| InventoryAdjusted | تعديل المخزون |
| StockReceived | استلام مخزون وارد |
حقول الطلب
خصائص مهمة في كائنات الطلب. رقم البوليصة / التتبع تحت shipping عند وجود حجز شحن.
أساسي
| الحقل | المعنى |
|---|---|
| id | معرّف الطلب الثابت |
| number | رقم الطلب الظاهر للتاجر |
| status | حالة دورة الحياة — انظر Enums |
| channel | مصدر إنشاء الطلب |
| customerId | معرّف العميل عند الربط |
| customerName | اسم المشتري |
| customerPhone | هاتف المشتري |
| shippingAddress | عنوان التسليم |
| lineItems[] | بنود الطلب |
| payment | الطريقة + مبلغ COD + العملة |
| totals | المجموع الفرعي والشحن والخصم والإجمالي |
| shipmentIds | معرّفات الشحنات المرتبطة |
| shipping | ملخص حجز الناقل (البوليصة) |
| notes | ملاحظات |
| tags | وسوم الطلب |
| createdAt / updatedAt | طوابع زمنية |
كائن shipping (البوليصة)
| الحقل | المعنى |
|---|---|
| shipping.shipmentId | معرّف شحنة وجهة |
| shipping.carrier | رمز الناقل (مثل mega) |
| shipping.trackingNumber | رقم البوليصة / التتبع من الناقل |
| shipping.waybill | نفس trackingNumber — اسم بديل للتكامل |
| shipping.megaShipmentId | معرّف الشحنة لدى الناقل عند الحجز |
| shipping.status | حالة الشحنة لدى الناقل |
| shipping.qrCode | بيانات QR عند توفرها |
| shipping.labelUrl | رابط الملصق عند التوفر |
| shipping.error | خطأ الحجز إن فشل |
| shipping.failureReason | سبب استثناء التسليم |
| shipping.awbPrinted | هل طُبعت البوليصة |
| shipping.estimatedDeliveryAt | الوصول المتوقع بعد الاستلام |
| shipping.deliveryTiming | on_time / early / late / pending |
بنود السطر
| الحقل | المعنى |
|---|---|
| variantId | معرّف المتغير |
| productId | معرّف المنتج الأب |
| sku | رمز المخزون |
| qty | الكمية |
| price | سعر البيع للوحدة |
| unitCost | تكلفة الوحدة عند الإنشاء |
| costSource | variant | stock | missing |
| imageUrl | صورة السطر عند التوفر |
DELETE /orders/:id يلغي الطلب عند السماح بالانتقال إلى cancelled — ولا يحذف السجل نهائيًا.
حقول المنتج
خصائص مهمة في كائنات المنتجات من /products.
المنتج
| الحقل | المعنى |
|---|---|
| id | معرّف المنتج |
| title | الاسم الظاهر |
| sku | SKU على مستوى المنتج عند الاستخدام |
| status | active | draft | archived |
| description / descriptionHtml | الوصف |
| tags | وسوم |
| collectionIds | العضوية في المجموعات |
| variants[] | المتغيرات القابلة للبيع |
| media / mediaUrls | الصور |
| trackInventory | هل يُتتبَّع المخزون |
| inventoryPolicy | سياسة النفاد (مثل deny) |
| storeQuantity | الكمية الإجمالية القابلة للبيع |
| categoryId / subcategoryId | تصنيف الكتالوج |
| brand | العلامة |
| price / currency | سعر العرض عند التوفر |
| importSource | مصدر الاستيراد عند الاستيراد (مثل vondera) |
| importExternalId | المعرّف الخارجي من مصدر الاستيراد |
| warehouseQuantity | مخزون مستودع وجهة عند الاشتراك |
| shopifyProductId | معرّف منتج Shopify عند الربط |
المتغير
| الحقل | المعنى |
|---|---|
| id | معرّف المتغير (لـ lineItems.variantId) |
| sku | SKU فريد للمتجر |
| title | عنوان المتغير |
| price | سعر البيع |
| compareAtPrice | السعر قبل الخصم |
| attributes | الخيارات مثل الحجم واللون |
| inventoryQuantity | الكمية المتاحة |
| cost | تكلفة الوحدة عند التعيين |
| barcode | الباركود عند التعيين |
| weightGrams | وزن المتغير عند التعيين |
| hsCode / taxCode | أكواد الجمارك / الضريبة عند التعيين |
حقول رسوم الشحن والرصيد
نقاط قراءة رسوم المناطق ورصيد محفظة / تسوية الشحن.
منطقة رسوم الشحن
| الحقل | المعنى |
|---|---|
| sectorId | معرّف المنطقة |
| name | اسم المنطقة |
| govId / govName | المحافظة |
| fee | رسوم التوصيل للعميل |
| currency | عملة الرسوم |
| isOverride | true عند تجاوز افتراضي وجهة |
| source | مصدر الرسوم |
| estimatedArrivalHours | ساعات الوصول المتوقعة |
الرصيد
| الحقل | المعنى |
|---|---|
| available | الرصيد المتاح |
| pending | المبلغ المعلّق |
| settled | المُسوّى عند التوفر |
| currency | عملة الرصيد |
| lowThreshold | حد التحذير |
| isLow | true عند انخفاض الرصيد |
| source | cached | live | simulated |
| syncedAt | آخر مزامنة |
مرّر refresh=1 على /balance لفرض مزامنة فورية من الناقل.
الطلبات
النطاق: orders.crud. DELETE يلغي الطلب (انتقال حالة) — لا يوجد حذف نهائي.
GET /api/v1/orders?pageSize=20
Authorization: Bearer wjh_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Accept: application/json{
"status": 200,
"message": "ok",
"data": {
"items": [
{
"id": "ord_01JABC",
"number": "10042",
"status": "pending",
"customerName": "Ahmed Hassan",
"customerPhone": "+201012345678",
"payment": { "method": "cod", "codAmount": 495, "currency": "EGP" },
"totals": { "subtotal": 450, "shipping": 45, "total": 495, "currency": "EGP" },
"createdAt": "2026-10-08T17:12:04.000Z"
}
],
"nextPageToken": null
}
}POST /api/v1/orders
Authorization: Bearer wjh_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Content-Type: application/json
{
"customerName": "Ahmed Hassan",
"customerPhone": "+201012345678",
"shippingAddress": {
"city": "Cairo",
"area": "Nasr City",
"line1": "12 Nile St",
"phone": "+201012345678"
},
"lineItems": [
{
"productId": "prd_01J",
"variantId": "var_01J",
"sku": "TEE-BLK-M",
"title": "Cotton tee",
"qty": 2,
"price": 225
}
],
"payment": { "method": "cod", "codAmount": 495, "currency": "EGP" }
}{
"status": 201,
"message": "created",
"data": {
"id": "ord_01JABC",
"number": "10042",
"status": "waiting_for_pickup",
"channel": "manual",
"customerName": "Ahmed Hassan",
"customerPhone": "+201012345678",
"lineItems": [
{
"variantId": "var_01J",
"productId": "prd_01J",
"sku": "TEE-BLK-M",
"title": "Cotton tee",
"qty": 2,
"price": 225,
"unitCost": 80,
"costSource": "variant"
}
],
"payment": { "method": "cod", "codAmount": 495, "currency": "EGP" },
"totals": { "subtotal": 450, "shipping": 45, "total": 495, "currency": "EGP" },
"shipmentIds": ["shp_01J"],
"shipping": {
"shipmentId": "shp_01J",
"carrier": "mega",
"status": "created",
"trackingNumber": "WJH123456789",
"waybill": "WJH123456789",
"megaShipmentId": "mega_987654",
"qrCode": "MEGA-QR-PAYLOAD",
"labelUrl": "https://labels.example.com/WJH123456789.pdf",
"awbPrinted": false
},
"createdAt": "2026-10-08T17:12:04.000Z"
}
}DELETE /api/v1/orders/ord_01JABC
Authorization: Bearer wjh_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx{
"status": 200,
"message": "cancelled",
"data": {
"id": "ord_01JABC",
"status": "cancelled"
}
}أيضًا: GET /api/v1/orders/:id و PATCH /api/v1/orders/:id.
المنتجات
النطاق: products.crud.
GET /api/v1/products?pageSize=20
Authorization: Bearer wjh_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Accept: application/json{
"status": 200,
"message": "ok",
"data": {
"items": [
{
"id": "prd_01J",
"title": "Cotton tee",
"status": "active",
"sku": "TEE-BLK",
"slug": "cotton-tee",
"brand": "Demo",
"categoryId": "apparel",
"subcategoryId": "tshirts",
"description": "Soft cotton tee",
"descriptionHtml": "<p>Soft cotton tee</p>",
"tags": ["basics"],
"collectionIds": ["col_01J"],
"seo": { "title": "Cotton tee", "description": "Soft cotton tee" },
"media": ["media/tee.jpg"],
"mediaUrls": ["https://cdn.example.com/media/tee.jpg"],
"trackInventory": true,
"inventoryPolicy": "deny",
"storeQuantity": 24,
"warehouseQuantity": 10,
"importSource": "vondera",
"importExternalId": "43254206",
"variants": [
{
"id": "var_01J",
"sku": "TEE-BLK-M",
"title": "M",
"price": 225,
"compareAtPrice": 275,
"cost": 80,
"barcode": "6220000000000",
"attributes": { "Color": "Black", "Size": "M" },
"inventoryQuantity": 12,
"weightGrams": 180
}
]
}
],
"nextPageToken": null,
"warehouseEnrolled": true
}
}POST /api/v1/products
Authorization: Bearer wjh_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Content-Type: application/json
{
"title": "Cotton tee",
"sku": "TEE-BLK",
"status": "active",
"description": "Soft cotton tee",
"variants": [
{
"sku": "TEE-BLK-M",
"price": 225,
"compareAtPrice": 275,
"cost": 80,
"attributes": { "Color": "Black", "Size": "M" },
"inventoryQuantity": 12
}
]
}{
"status": 201,
"message": "created",
"data": {
"id": "prd_01J",
"title": "Cotton tee",
"sku": "TEE-BLK",
"status": "active",
"storeQuantity": 12,
"variants": [
{
"id": "var_01J",
"sku": "TEE-BLK-M",
"price": 225,
"compareAtPrice": 275,
"cost": 80,
"attributes": { "Color": "Black", "Size": "M" },
"inventoryQuantity": 12
}
]
}
}أيضًا: GET / PATCH / DELETE /api/v1/products/:id.
المجموعات
النطاق: collections.crud. مجموعات منتجات التاجر (وليس تصنيفات الكتالوج الثابتة).
GET /api/v1/collections
Authorization: Bearer wjh_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Accept: application/json{
"status": 200,
"message": "ok",
"data": {
"items": [
{ "id": "col_01J", "title": "Summer", "slug": "summer", "status": "active" }
],
"nextPageToken": null
}
}POST /api/v1/collections
Authorization: Bearer wjh_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Content-Type: application/json
{
"title": "Summer",
"slug": "summer",
"status": "active",
"productIds": ["prd_01J"]
}{
"status": 201,
"message": "created",
"data": {
"id": "col_01J",
"title": "Summer",
"slug": "summer",
"status": "active",
"productIds": ["prd_01J"]
}
}المصروفات
النطاق: expenses.crud.
GET /api/v1/expenses?from=2026-10-01&to=2026-10-31
Authorization: Bearer wjh_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Accept: application/json{
"status": 200,
"message": "ok",
"data": {
"items": [
{
"id": "exp_01J",
"categoryName": "Rent",
"amount": 5000,
"currency": "EGP",
"expenseDate": "2026-10-01"
}
]
}
}POST /api/v1/expenses
Authorization: Bearer wjh_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Content-Type: application/json
{
"categoryId": "cat_rent",
"amount": 5000,
"currency": "EGP",
"expenseDate": "2026-10-01",
"vendor": "Landlord Co",
"notes": "October rent",
"recurringRule": "monthly"
}{
"status": 201,
"message": "created",
"data": {
"id": "exp_01J",
"categoryId": "cat_rent",
"categoryName": "Rent",
"amount": 5000,
"currency": "EGP",
"expenseDate": "2026-10-01",
"vendor": "Landlord Co",
"recurringRule": "monthly"
}
}أيضًا: GET / PATCH / DELETE /api/v1/expenses/:id.
التحليلات
النطاق: analytics.read. مرّر from / to بصيغة YYYY-MM-DD.
GET /api/v1/analytics?from=2026-10-01&to=2026-10-17
Authorization: Bearer wjh_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Accept: application/json{
"status": 200,
"message": "ok",
"data": {
"from": "2026-10-01",
"to": "2026-10-17",
"summary": {
"orders": 128,
"revenue": 64250,
"currency": "EGP"
},
"series": [
{ "day": "2026-10-01", "orders": 8, "revenue": 4200 }
]
}
}أكواد الخصم
النطاق: discounts.read (قراءة فقط).
GET /api/v1/discounts
Authorization: Bearer wjh_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Accept: application/json{
"status": 200,
"message": "ok",
"data": {
"items": [
{
"id": "dsc_01J",
"code": "SUMMER10",
"type": "percent",
"value": 10,
"active": true
}
]
}
}أيضًا: GET /api/v1/discounts/:id.
العملاء
النطاق: customers.read (أو customers.crud). قراءة فقط على /api/v1.
GET /api/v1/customers?pageSize=20
Authorization: Bearer wjh_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Accept: application/json{
"status": 200,
"message": "ok",
"data": {
"items": [
{
"id": "cus_01JDEF",
"name": "Ahmed Hassan",
"phone": "+201012345678",
"email": "ahmed@example.com"
}
],
"nextPageToken": null
}
}أيضًا: GET /api/v1/customers/:id.
التقييمات
النطاق: reviews.read.
GET /api/v1/reviews?status=approved&limit=20
Authorization: Bearer wjh_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Accept: application/json{
"status": 200,
"message": "ok",
"data": {
"items": [
{
"id": "rev_01J",
"productId": "prd_01J",
"rating": 5,
"body": "Great quality",
"status": "approved",
"createdAt": "2026-10-08T10:00:00.000Z"
}
]
}
}أيضًا: GET /api/v1/reviews/:id.
رسوم شحن العميل
النطاق: shipping_fees.read. رسوم المناطق المفروضة على العملاء (افتراضيات وجهة/ميجا + تجاوزات المتجر).
GET /api/v1/shipping-fees?govId=cairo&limit=50
Authorization: Bearer wjh_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Accept: application/json{
"status": 200,
"message": "ok",
"data": {
"currency": "EGP",
"megaDefaultsSource": "work_places",
"items": [
{
"sectorId": "sec_nasr_city",
"name": "Nasr City",
"govId": "cairo",
"govName": "Cairo",
"fee": 45,
"currency": "EGP",
"isOverride": false,
"source": "wijha_default",
"estimatedArrivalHours": 48
}
]
}
}GET /api/v1/shipping-fees/resolve?sectorId=sec_nasr_city
Authorization: Bearer wjh_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Accept: application/json{
"status": 200,
"message": "ok",
"data": {
"sectorId": "sec_nasr_city",
"fee": 45,
"currency": "EGP",
"isOverride": false,
"source": "wijha_default",
"estimatedArrivalHours": 48
}
}رصيد الشحن
النطاق: balance.read. رصيد التسوية / المحفظة لحساب شحن المتجر.
GET /api/v1/balance
Authorization: Bearer wjh_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Accept: application/json
# Optional: force a fresh sync from the carrier
# GET /api/v1/balance?refresh=1{
"status": 200,
"message": "ok",
"data": {
"available": 1250.5,
"pending": 320,
"settled": 0,
"currency": "EGP",
"lowThreshold": 200,
"isLow": false,
"source": "cached",
"syncedAt": "2026-10-08T12:00:00.000Z"
}
}معلومات المتجر
النطاق: store.read. ملف عام آمن فقط (بدون أسرار شركة الشحن).
GET /api/v1/store
Authorization: Bearer wjh_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Accept: application/json{
"status": 200,
"message": "ok",
"data": {
"id": "str_01JSTORE",
"name": "Demo Store",
"slug": "demo-store",
"status": "active",
"country": "EG",
"timezone": "Africa/Cairo",
"currency": "EGP",
"contact": {
"supportEmail": "hello@example.com",
"supportPhone": "+201012345678",
"tagline": "Quality basics",
"logoUrl": "https://cdn.example.com/logo.png"
},
"createdAt": "2026-01-01T00:00:00.000Z",
"updatedAt": "2026-10-08T12:00:00.000Z"
}
}Webhooks (عبر الواجهة)
النطاقات: webhooks.read للعرض؛ webhooks.write للإنشاء. نفس نقاط بوابة المطورون → Webhooks.
GET /api/v1/webhooks/endpoints
Authorization: Bearer wjh_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Accept: application/json{
"status": 200,
"message": "ok",
"data": {
"items": [
{
"id": "wh_01J",
"nickname": "ERP sync",
"url": "https://example.com/wijha/hooks",
"events": ["OrderCreated", "OrderStatusChanged"],
"active": true,
"hasSecret": true
}
],
"nextPageToken": null
}
}POST /api/v1/webhooks/endpoints
Authorization: Bearer wjh_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Content-Type: application/json
{
"url": "https://example.com/wijha/hooks",
"method": "POST",
"nickname": "ERP sync",
"events": ["OrderCreated", "OrderStatusChanged"],
"secret": "super-secret-min-8",
"active": true
}{
"status": 201,
"message": "created",
"data": {
"id": "wh_01J",
"url": "https://example.com/wijha/hooks",
"method": "POST",
"nickname": "ERP sync",
"events": ["OrderCreated", "OrderStatusChanged"],
"hasSecret": true,
"secret": "super-secret-min-8",
"active": true,
"status": "active"
}
}أيضًا: GET /api/v1/webhooks/events و GET /api/v1/webhooks/endpoints/:id.