إنتقل إلى المحتوى الرئيسي

المنتجات

المنتج هو الوحدة الأساسية القابلة للبيع في المتجر. كل نداءات المنتجات مقيّدة بمتجر المفتاح المنادي — لا يمكنك أبدًا الوصول إلى بيانات تاجر آخر بالخطأ.

GET /v1/products​

قائمة المنتجات. ترقيم بالمؤشّر. تُخدَم حديثًا في كل نداء: طلب GET المُرسَل مباشرة بعد الكتابة يُرجع القيم الجديدة.

المصادقة: مفتاح منصة بصلاحية products:read (ممنوحة افتراضيًا). المفتاح الذي لا يحملها يحصل على 403 forbidden.

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

المعاملالنوعالافتراضيملاحظات
limitint (1–200)50حجم الصفحة
cursorstring—من next_cursor لاستجابة سابقة
statusactive | draft | archived—تصفية حسب الحالة
searchstring—يطابق name (LIKE) وsku بالضبط

القيمة غير المعروفة في status تُتجاهَل بدل أن تُرفَض — فتحصل على القائمة غير المصفّاة، وهي تشمل المنتجات بحالة archived. صفِّ صراحةً إن أردت العناصر الحيّة فقط.

الطلب​

curl 'https://api.dzbuild.app/v1/products?limit=10&status=active' \
-H "Authorization: Bearer $DZ_KEY"

الاستجابة 200​

{
"data": {
"items": [
{
"id": 26,
"name": "PRO",
"slug": "pro",
"short_description": null,
"price": 1000,
"compare_price": null,
"sku": "",
"stock_quantity": 0,
"track_stock": false,
"status": "active",
"has_variants": true,
"featured": false,
"primary_image": "https://cdn.dzbuild.app/uploads/products/123/123_1700000000_example.webp",
"created_at": "2026-01-13 15:06:06",
"updated_at": "2026-01-13 15:12:32"
}
],
"next_cursor": null,
"has_more": false
},
"meta": { "request_id": "...", "api_version": "v1" }
}
تغيّر في v1.1 — روابط الصور صارت كاملة

صار primary_image (وكذلك images[].url في GET /v1/products/{id}) رابط CDN كاملًا جاهزًا للاستعمال كما هو. قبل v1.1 كان الاثنان يُرجعان اسم ملف مجرّدًا على المستدعي أن يضيف إليه البادئة بنفسه. فإن كان تكاملك يبني البادئة يدويًا، احذف ذلك المنطق — فالقيمة تبدأ أصلًا بـ https://.

GET /v1/products/{id}​

تفاصيل المنتج كاملة بما فيها الصور والمتغيرات.

المصادقة: مفتاح منصة بصلاحية products:read.

الطلب​

curl https://api.dzbuild.app/v1/products/26 \
-H "Authorization: Bearer $DZ_KEY"

الاستجابة 200​

{
"data": {
"id": 26,
"name": "PRO",
"slug": "pro",
"description": "- Single store\n- Up to 300 products\n- ...",
"short_description": null,
"category_id": null,
"pricing": {
"price": 1000,
"compare_price": null,
"cost_price": null
},
"inventory": {
"sku": "",
"barcode": null,
"track_stock": false,
"stock_quantity": 0,
"low_stock_alert": 5
},
"shipping": {
"weight": null, "height": null, "width": null, "length": null,
"do_insurance": false
},
"status": "active",
"featured": false,
"has_variants": true,
"images": [
{ "id": 28, "url": "https://cdn.dzbuild.app/uploads/products/123/123_1700000000_example.webp",
"alt_text": "Front view", "is_primary": true, "sort_order": 0 }
],
"variants": [
{
"id": 11,
"name": "Duration",
"type": "text",
"required": true,
"sort_order": 0,
"options": [
{ "id": 14, "value": "30 days", "color_code": null, "price_adjustment": 0,
"stock": null, "sku": null, "image_id": null, "show_as_card": false,
"sort_order": 0, "is_active": true },
{ "id": 15, "value": "90 days", "color_code": null, "price_adjustment": 500,
"stock": null, "sku": null, "image_id": null, "show_as_card": false,
"sort_order": 1, "is_active": true }
]
}
],
"combinations": [],
"combination_count": 0,
"combinations_truncated": false,
"created_at": "2026-01-13 15:06:06",
"updated_at": "2026-01-13 15:12:32"
}
}
أُضيف في v1.1

images[].alt_text، وحقول الخيار الكاملة (price_adjustment وsku وshow_as_card وsort_order وis_active)، وrequired / sort_order على مستوى المجموعة، وكتلة combinations بأكملها — كلها جديدة. تسرد combinations 300 مدخلة كحد أقصى، بينما يبقى combination_count دائمًا الإجمالي الحقيقي، وcombinations_truncated يخبرك متى اقتُطعت القائمة.

POST /v1/products — إنشاء​

المصادقة: مفتاح منصة بصلاحيتَي products:write وproducts:read. الرد هو المنتج كما يُرجعه GET /v1/products/{id}، لذا فالمفتاح الذي لا يحمل products:read يحصل على 403 forbidden رغم أن المنتج أُنشئ. يتطلب Idempotency-Key.

الجسم​

الحقلالنوعإلزاميملاحظات
namestring (1–255)✅
pricenumber ≥ 0✅بالدينار الجزائري
compare_pricenumber ≥ 0 | nullالسعر المشطوب
cost_pricenumber ≥ 0 | nullللتاجر فقط — لا يُعرض للعميل
descriptionstringوصف طويل، يقبل الأسطر وتنسيق HTML (خط عريض، قوائم، عناوين، روابط، جداول، صور)؛ وتُحذف السكربتات وغيرها من الوسوم غير الآمنة. الحد الأقصى 60000 بايت: ما يتجاوزه يُرجع bad_request "description exceeds 60000 bytes".
short_descriptionstring ≤ 500عبارة قصيرة
skustring ≤ 100SKU داخلي
barcodestring ≤ 100UPC/EAN
weightnumberكغ، للشحن
shipping_height / width / lengthnumberسم
do_insuranceboolفرض تأمين الشحن لهذا المنتج
track_stockboolالافتراضي false
stock_quantityint ≥ 0إن كان track_stock
low_stock_alertint ≥ 0الافتراضي 5. يقود شارة "مخزون منخفض" في لوحة التحكم.
variant_stock_enabledboolتتبّع المخزون لكل خيار متغيّر (أحمر، L، …)
combination_stock_enabledboolتتبّع المخزون لكل تركيبة متغيرات (أحمر+L). يستلزم variant_stock_enabled.
category_idintيجب أن يكون موجودًا في متجرك. ينضمّ المنتج إلى هذه الفئة وتصير فئته الرئيسية، مع الإبقاء على الفئات التي ينتمي إليها أصلًا. وفي PATCH تمسح القيمة null الفئة الرئيسية دون إخراج المنتج من أي فئة.
statusactive | draft | archivedالافتراضي draft
featuredboolالافتراضي false

عند تفعيل variant_stock_enabled أو combination_stock_enabled، يُعطَّل track_stock تلقائيًا (المتغيرات تتحكم بمخزونها).

نادرًا ما تحتاج إلى ضبط هذين الحقلين مباشرةً: فـ PUT /v1/products/{id}/variants يضبطهما لك انطلاقًا من الحمولة التي ترسلها (مخزون لكل خيار أو تركيبات).

حد الخطة​

Free: 5 منتجات نشطة. Pro: 300. Unlimited / Enterprise: غير محدود. يحسب فقط المنتجات ذات الحالة active، والمسوّدات لا تُحسب، ويجري العدّ لحظيًا عند كل نداء. الفحص يجري عند الإنشاء فقط: تحويل مسوّدة موجودة إلى active عبر PATCH لا يُحجب أبدًا، فيمكن لمتجر على الخطة المجانية تجاوز 5 منتجات نشطة بهذه الطريقة. ولأن الفحص يجري عند كل إنشاء، لا يمكن لمتجر بلغ حدّه إنشاء منتج جديد حتى مع status: "draft". واسم خطة غير معروف يعود إلى الحد المجاني وهو 5. عند بلوغ الحد:

{ "error": { "code": "bad_request",
"message": "Plan 'free' allows at most 5 active products. Upgrade to add more." } }

الطلب​

curl -X POST 'https://api.dzbuild.app/v1/products' \
-H "Authorization: Bearer $DZ_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"name": "T-shirt - Cotton 200gsm",
"price": 1500,
"compare_price": 1900,
"description": "100% cotton, made in Algeria.",
"sku": "TS-COT-200",
"stock_quantity": 50,
"track_stock": true,
"status": "draft"
}'

الاستجابة 200​

الإنشاء الناجح يُرجع HTTP 200 (وليس 201) بنفس جسم GET /v1/products/{id}. لا تتفرّع على status === 201 — تحقّق من data.id بدلًا من ذلك. id وslug وcreated_at صارت معبّأة.

عند الإنشاء يُشتقّ slug دائمًا من name — وأي slug في الجسم يُتجاهل. لتعيين slug محدّد، أنشئ أولًا ثم استعمل PATCH /v1/products/{id} مع {"slug":"…"}. التطبيع يحوّل إلى حروف صغيرة ويستبدل كل تتابع من المحارف غير الحرفية وغير الرقمية بـ - (مدرك لليونيكود — الحروف العربية والمشكّلة تُحفَظ، فهو إذًا ليس [a-z0-9-])، مع الاقتطاع عند 200 حرف؛ والتعارضات تأخذ لواحق -2 و-3 وهكذا.

الأخطاء​

الكودالسبب
bad_request "Body must be valid JSON"Content-Type خاطئ أو JSON تالف
bad_request "name is required (1-255 chars)"الاسم مفقود أو طويل جدًا
bad_request "price must be a non-negative number"سعر غير صالح
bad_request "category_id N does not belong to this store"فئة من متجر آخر
bad_request "Plan 'free' allows at most …"تجاوز حد الخطة

PATCH /v1/products/{id} — تعديل​

المصادقة: مفتاح منصة بصلاحيتَي products:write وproducts:read. الرد هو المنتج المحدَّث، لذا فالمفتاح الذي لا يحمل products:read يحصل على 403 forbidden رغم أن التعديل حُفظ. يتطلب Idempotency-Key.

تحديث جزئي — أرسل فقط الحقول التي تريد تغييرها. الحقول غير المرسلة تُحفظ كما هي.

curl -X PATCH 'https://api.dzbuild.app/v1/products/26' \
-H "Authorization: Bearer $DZ_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{ "price": 1200, "status": "active" }'

تُرجع 200 والمنتج الكامل المحدَّث. إذا لم يكن المنتج موجودًا (أو ينتمي لمتجر آخر) ستحصل على 404 not_found.

عند تغيير الاسم عبر PATCH { name: ... } يُعاد توليد الـ slug تلقائيًا فقط إن لم تُرسل slug صراحةً. أرسل slug للحفاظ على رابط محدد بعد إعادة التسمية.

DELETE /v1/products/{id}​

المصادقة: مفتاح منصة بصلاحية products:write. يتطلب Idempotency-Key.

curl -X DELETE 'https://api.dzbuild.app/v1/products/26' \
-H "Authorization: Bearer $DZ_KEY" \
-H "Idempotency-Key: del-26-2026-04-30"

الاستجابة:

{ "data": { "deleted": true, "id": 26 } }

هذا حذف نهائي: يُحذف المنتج ومعه صوره ومتغيراته وعروضه وإضافاته (add-ons) وتركيباته وآراء العملاء عليه. أما ملفات الصور المخزَّنة فلا يحذفها هذا النداء، لذا قد يبقى رابط صورة حفظته سابقًا يعمل.

الحذف يفصل السجلّ، والمنتج المرتبط بصفحة هبوط لا يُحذف

الطلبات السابقة تحتفظ ببنودها، ويبقى اسم المنتج و SKU والسعر كما سُجّلت وقت الشراء، فتظل الطلبات القديمة مقروءة، لكن البند لم يعد مرتبطًا بمنتج (product_id يصبح null). المنتج الذي تستخدمه صفحة هبوط (على مستوى الصفحة، أو في قسم استمارة طلب أو زر طلب أو عروض منتج) يُرفض حذفه بـ 409 product_in_use_by_landing_page؛ وتسرد بيانات الخطأ الصفحات في landing_pages[] مع id و title و slug. احذف صفحة الهبوط أولًا (DELETE /v1/landing-pages/{id}) أو اربطها بمنتج آخر (PATCH /v1/landing-pages/{id} مع product_id جديد)، ثم احذف المنتج. يُفضّل PATCH { "status": "archived" } على الحذف.

POST /v1/products/{id}/images — إضافة صورة​

أُضيف في v1.1. المصادقة: مفتاح منصة بصلاحية products:write. يتطلب Idempotency-Key.

أنت تُعطي رابط https عموميًا؛ وتتولّى DZBuild تنزيل الصورة من جهة الخادم وتحويلها وتحسينها ثم استضافتها على شبكة CDN الخاصة بالمتجر. لا يوجد رفع للملفات عبر الواجهة البرمجية — تكفي الإشارة إلى الصورة برابط ونحن نجلبها.

الجسم​

الحقلالنوعإلزاميملاحظات
urlstring ≤ 2000✅رابط https:// عمومي لملف الصورة
alt_textstring ≤ 255نص لإمكانية الوصول وتحسين محركات البحث
is_primaryboolاجعل هذه الصورة هي الصورة الرئيسية للمنتج
curl -X POST 'https://api.dzbuild.app/v1/products/26/images' \
-H "Authorization: Bearer $DZ_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{ "url": "https://example.com/tshirt-front.jpg", "alt_text": "T-shirt front" }'
{ "data": { "image": { "id": 88,
"url": "https://cdn.dzbuild.app/uploads/products/123/123_1700000001_example.webp",
"alt_text": "T-shirt front", "is_primary": true, "sort_order": 0,
"file_size": 27652, "width": 1000, "height": 1000 },
"deduplicated": false } }

قواعد يجدر معرفتها:

  • أول صورة للمنتج تصير تلقائيًا الصورة الرئيسية.
  • إرسال رابط بايتاته مرفقة أصلًا بالمنتج لا يُنشئ نسخة مكرّرة — بل تستعيد الصورة الموجودة مع "deduplicated": true (وHTTP 200 بدل 201).
  • الصيغ المقبولة: JPEG وPNG وWebP وGIF وBMP وAVIF وHEIC/HEIF وTIFF. الحد الأقصى 20 ميغابايت و10000×10000 بكسل. تُحوَّل الصور إلى WebP (مع إزالة بيانات EXIF)، والصورة التي يزيد عرضها على 2000 بكسل يُصغَّر عرضها إلى 2000 بكسل مع الحفاظ على تناسب أبعادها. أما ملف WebP بحجم 3 ميغابايت أو أقل وعرض لا يتجاوز 2000 بكسل فيُخزَّن كما أُرسل.
  • حد أقصى 20 صورة لكل منتج.

أي الروابط تُقبل​

لأسباب أمنية، لا يقبل الجالب إلا العناوين العمومية ولا يتبع أي إعادة توجيه. يُرفَض الرابط (url_refused) إذا لم يكن https، أو حمل بيانات اعتماد (https://user:pass@…)، أو استعمل منفذًا غير 443، أو كان عنوان IP بدل اسم مضيف، أو أدّى إلى عنوان خاص أو داخلي أو عنوان بيانات وصفية سحابية. أما الرابط الذي يردّ بإعادة توجيه أو بحالة خطأ فيفشل بالخطأ image_fetch_failed، والرابط الذي يردّ بصفحة ويب (صفحة تسجيل دخول مثلًا) أو بأي ملف آخر ليس صورة مدعومة فيفشل بالخطأ unsupported_image.

الأخطاء​

الكودHTTPالسبب
validation_error422url مفقود أو أطول من 2000 حرف
url_refused422رابط مرفوض حسب القواعد أعلاه
image_fetch_failed422المضيف غير متاح، أو إعادة توجيه، أو استجابة غير 200
unsupported_image422ليس صورة (صفحة ويب مثلًا)، أو صيغة غير مدعومة، أو أبعاد خارج المجال
image_too_large422أكبر من 20 ميغابايت
too_many_images422المنتج يحتوي أصلًا على 20 صورة
not_found404المنتج ليس في متجرك

PATCH /v1/products/{id}/images/{image_id}​

أُضيف في v1.1. لتعديل alt_text أو sort_order (0–999)، أو لترقية الصورة إلى رئيسية عبر is_primary: true.

curl -X PATCH 'https://api.dzbuild.app/v1/products/26/images/88' \
-H "Authorization: Bearer $DZ_KEY" -H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{ "is_primary": true }'

يحتفظ كل منتج دائمًا بصورة رئيسية واحدة بالضبط، لذا يُرفض is_primary: false بالخطأ primary_required — رقِّ صورة أخرى بدلًا من ذلك.

DELETE /v1/products/{id}/images/{image_id}​

أُضيف في v1.1. يحذف سجلّ الصورة وملفاتها المخزَّنة.

{ "data": { "deleted": true, "new_primary_image_id": 89,
"variant_references_cleared": 2, "remaining_images": 3 } }

إن كانت خيارات متغيرات تشير إلى هذه الصورة، فتُمسح تلك الروابط (وتبقى الخيارات نفسها موجودة) — وvariant_references_cleared يخبرك بعددها. وحذف الصورة الرئيسية يُرقّي الصورة التالية تلقائيًا.

PUT /v1/products/{id}/variants — استبدال المتغيرات​

أُضيف في v1.1. المصادقة: مفتاح منصة بصلاحية products:write. ترويسة Idempotency-Key اختيارية: معها تحصل إعادة المحاولة بنفس القيمة على الرد الأول، ومن دونها يُعاد تنفيذ الاستبدال الكامل في كل نداء.

هذا يستبدل كل متغيرات المنتج

لا يوجد تحديث جزئي للمتغيرات. اقرأ الحالة الحالية عبر GET /v1/products/{id} وأعد إرسال كل ما تريد الاحتفاظ به — فكل ما تحذفه من الحمولة يُحذف فعليًا. أرسل {"groups": []} لإزالة جميع المتغيرات.

الجسم​

الحقلالنوعإلزاميملاحظات
groupsarray✅مجموعات المتغيرات بترتيب العرض. و[] تمسح كل المتغيرات.
groups[].namestring ≤ 100✅مثل Color أو Size. فريد داخل المنتج.
groups[].typetext | color | image_text | selectable | dropdownالافتراضي text. وselectable تعني مجموعة إضافات اختيارية متعددة الاختيار. وdropdown تعني خيارات نصية تظهر في قائمة منسدلة.
groups[].requiredboolالافتراضي true (ودائمًا false مع selectable)
groups[].options[].namestring ≤ 100✅فريد داخل المجموعة
groups[].options[].color_code#rrggbbلمجموعات color
groups[].options[].price_adjustmentnumberيُضاف إلى السعر الأساسي (أو يُطرح منه)
groups[].options[].stockint ≥ 0 | nullمخزون لكل خيار
groups[].options[].skustring ≤ 100SKU لكل خيار
groups[].options[].image_idintيجب أن يكون صورة موجودة لهذا المنتج
groups[].options[].show_as_cardboolعرض الخيار على شكل بطاقة صورة
combinationsarrayمخزون لكل تركيبة (يحتاج مجموعتين أو أكثر من غير نوع selectable)
combinations[].optionsobject✅{ "Color": "Red", "Size": "L" } — مدخلة واحدة لكل مجموعة من غير نوع selectable
combinations[].stockint ≥ 0✅
combinations[].skustring ≤ 100
combinations[].is_activeboolالافتراضي true

الحدود: 10 مجموعات، و100 خيار لكل مجموعة، و200 خيار إجمالًا، و1000 تركيبة.

curl -X PUT 'https://api.dzbuild.app/v1/products/26/variants' \
-H "Authorization: Bearer $DZ_KEY" -H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"groups": [
{ "name": "Color", "type": "color", "options": [
{ "name": "Red", "color_code": "#ff0000", "image_id": 88 },
{ "name": "Blue", "color_code": "#0000ff" } ] },
{ "name": "Size", "type": "text", "options": [
{ "name": "L" }, { "name": "XL", "price_adjustment": 100 } ] }
],
"combinations": [
{ "options": { "Color": "Red", "Size": "L" }, "stock": 5, "sku": "TS-R-L" },
{ "options": { "Color": "Blue", "Size": "XL" }, "stock": 2 }
]
}'

تُرجع كتلتَي variants وcombinations الجديدتين (بنفس شكل GET /v1/products/{id}).

وضع المخزون يُضبط لك تلقائيًا​

  • عند إرسال تركيبات: مخزون لكل تركيبة (combination_stock_enabled)، مع تعطيل track_stock على مستوى المنتج.
  • بلا تركيبات لكن الخيارات تحمل stock: مخزون لكل خيار (variant_stock_enabled)، مع تعطيل track_stock.
  • لا هذا ولا ذاك: المتغيرات للعرض فقط، ويبقى المخزون على مستوى المنتج يعمل كالمعتاد.

الأخطاء​

الكودHTTPالسبب
validation_error422أسماء أو أنواع أو ألوان أو أرقام غير صالحة، أو تجاوز أحد الحدود
invalid_image_id422image_id ليس صورة لهذا المنتج
combinations_not_applicable422أُرسلت تركيبات مع أقل من مجموعتين من غير نوع selectable
duplicate_combination422تركيبتان بنفس مجموعة الخيارات
not_found404المنتج ليس في متجرك

يجري التحقق من الصحة قبل حذف أي شيء — فالحمولة المرفوضة تترك متغيراتك الحالية دون أي مساس.

GET /v1/products/{id}/stock​

يقرأ وضع مخزون المنتج والعدد الحالي لكل هدف يمكنك ضبطه في هذا الوضع. اقرأه قبل أي مزامنة للمخزون لتحصل على معرّفات الخيارات والتركيبات. وعلى خلاف GET /v1/products، هذه القراءة غير مخزَّنة مؤقتًا، فتُظهر أي تعديل في الحال.

المصادقة: مفتاح منصة بصلاحية products:read.

curl https://api.dzbuild.app/v1/products/30/stock \
-H "Authorization: Bearer $DZ_KEY"
{
"data": {
"product_id": 30,
"mode": "variant_options",
"track_stock": false,
"flags": { "variant_stock_enabled": true, "combination_stock_enabled": false },
"max_stock": 9999999,
"options": [
{ "target": "option", "id": 41, "group": "Size", "value": "M",
"stock": null, "unlimited": true },
{ "target": "option", "id": 42, "group": "Size", "value": "L",
"stock": 10, "unlimited": false }
]
}
}

يبيّن mode أين يُحسب مخزون المنتج، ويسرد الرد أهداف هذا الوضع:

modeيُحسب المخزون علىما يظهر في الرد
productالمنتج نفسهproduct.stock_quantity
variant_optionsكل خيار من خيارات المتغيراتoptions[]
combinationsكل تركيبة من الخياراتcombinations[] مع sku وis_active وoptions (اسم المجموعة مقابل اسم الخيار). وتظهر options[] أيضًا، للقراءة فقط.

الخيار الذي يحمل "stock": null و"unlimited": true مخزونه غير محدود. ويتبع الوضعُ المتغيراتِ المحفوظة عبر PUT /v1/products/{id}/variants (انظر أعلاه).

POST /v1/products/{id}/stock​

يضبط أعداد المخزون أو يغيّرها. المصادقة: مفتاح منصة بصلاحية products:write. يتطلب Idempotency-Key.

الجسم​

تسرد items من 1 إلى 500 هدف. يحمل كل عنصر واحدًا فقط من set أو delta أو "unlimited": true، ولا يظهر الهدف الواحد إلا مرة واحدة في النداء.

الحقلالنوعإلزاميملاحظات
items[].targetproduct | option | combination✅يجب أن يطابق mode الخاص بالمنتج: product، أو option مع variant_options، أو combination مع combinations
items[].idint ≥ 1✅ مع option وcombinationالمعرّف الذي يُرجعه GET /v1/products/{id}/stock
items[].setint، من 0 إلى 9999999العدد الجديد
items[].deltaint، ليس 0الوحدات المضافة، وبقيمة سالبة للإنقاص. تبقى النتيجة بين 0 و9999999.
items[].unlimitedboolللخيارات فقط. true تجعل مخزون الخيار غير محدود. والخيار غير المحدود حاليًا يحتاج "unlimited": false إلى جانب set ليبدأ عدّه.

أي set أو delta على هدف product يفعّل أيضًا track_stock، فيحسب المتجر مخزون هذا المنتج من تلك اللحظة.

curl -X POST 'https://api.dzbuild.app/v1/products/30/stock' \
-H "Authorization: Bearer $DZ_KEY" -H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"items": [
{ "target": "option", "id": 41, "set": 20, "unlimited": false },
{ "target": "option", "id": 42, "delta": -2 }
]
}'

تُرجع 200 مع المخزون بعد التعديل، بنفس شكل GET أعلاه: الخيار 41 صار 20 والخيار 42 صار 8. تُحفظ العناصر معًا: إن رُفض أحدها لا يُحفظ أي منها. ويُسجَّل التعديل في سجل تعديلات المتجر (GET /v1/changes)، وPOST /v1/changes/{id}/undo يُرجع الأعداد السابقة.

الأخطاء​

الكودHTTPالسبب
validation_error422items غائبة أو فارغة أو تتجاوز 500؛ أو target أو id أو set أو delta غير صالح؛ أو الهدف نفسه مرتين؛ أو عنصر لا يحمل واحدًا فقط من set وdelta و"unlimited": true؛ أو "unlimited": true على منتج أو تركيبة
option_stock_unlimited422مخزون الخيار غير محدود حاليًا: set دون "unlimited": false، أو أي delta
stock_mode_mismatch409target لا يطابق وضع مخزون المنتج
not_found404المنتج ليس في متجرك، أو الخيار أو التركيبة ليست من هذا المنتج

GET /v1/products/{id}/offers​

يقرأ عروض الكمية للمنتج، أي الحزم التي تظهر في صفحة المنتج.

المصادقة: مفتاح منصة بصلاحية products:read.

curl https://api.dzbuild.app/v1/products/26/offers \
-H "Authorization: Bearer $DZ_KEY"
{
"data": {
"product_id": 26,
"pricing_note": "price is the TOTAL for the whole bundle of `quantity` units, not a unit price.",
"offers": [
{ "id": 51, "title": "Buy 2, get 1 free", "quantity": 3, "price": 2000,
"compare_price": 3000, "discount_type": null, "discount_value": null,
"badge_text": "Best value", "badge_color": "#10b981", "free_shipping": true,
"image_path": null, "image_url": null, "sort_order": 0, "is_active": true },
{ "id": 52, "title": "Pack of 2", "quantity": 2, "price": 0,
"compare_price": null, "discount_type": "percent", "discount_value": 10,
"badge_text": null, "badge_color": "#10b981", "free_shipping": false,
"image_path": null, "image_url": null, "sort_order": 1, "is_active": true }
]
}
}
price هو سعر الحزمة كاملة

price هو ما يدفعه الزبون مقابل كل وحدات quantity معًا، وليس سعر الوحدة. على منتج بـ 1000 دج، عرض «اشترِ 2 والثالثة مجانًا» يكون "quantity": 3, "price": 2000. والعرض الذي يحمل discount_type يُخزَّن فيه price بـ 0 ويُطرح discount_value من سعر المنتج مضروبًا في الكمية: amount يطرح مبلغًا بالدينار، وpercent يطرح نسبة مئوية ويطبّقها أيضًا على فروق أسعار المتغيرات. العرض الثاني أعلاه يبيع وحدتين بـ 1800 دج.

image_url هو رابط CDN الكامل لصورة العرض، وnull إن لم تكن له صورة.

POST /v1/products/{id}/offers​

يستبدل عروض الكمية للمنتج. المصادقة: مفتاح منصة بصلاحية products:write. يتطلب Idempotency-Key.

هذا يستبدل كل عروض المنتج

أرسل كل عرض تريد الاحتفاظ به، بالترتيب الذي تظهر به العروض. كل ما تحذفه من الحمولة يُحذف فعليًا. أرسل {"offers": []} لإزالة كل العروض.

الجسم​

الحقلالنوعإلزاميملاحظات
offersarray (من 0 إلى 50)✅العروض بالترتيب الذي تظهر به. و[] تزيل كل العروض.
offers[].titlestring✅يُقصّ إلى 255 حرفًا
offers[].quantityint، من 1 إلى 9999✅عدد الوحدات في الحزمة. يحتاج كل عرض كمية مختلفة.
offers[].pricenumber > 0✅ دون discount_typeالسعر الإجمالي للحزمة كلها، بالدينار. يُتجاهل عند ضبط discount_type.
offers[].discount_typeamount | percent | nullالافتراضي null (سعر ثابت للحزمة)
offers[].discount_valuenumber > 0✅ مع discount_typeبالدينار مع amount، و100 كحد أقصى مع percent
offers[].compare_pricenumber ≥ 0 | nullالسعر المشطوب، بالدينار
offers[].badge_textstringيُقصّ إلى 100 حرف
offers[].badge_color#rgb أو #rrggbbالافتراضي #10b981
offers[].free_shippingboolالتوصيل مجاني عندما يطلب الزبون هذا العرض. الافتراضي false.
offers[].image_pathstringيحتفظ بصورة العرض: أعد إرسال image_path الذي أرجعه GET
offers[].is_activeboolالافتراضي true

لا يمكن رفع صور العروض عبر الـ API: أضفها من لوحة التحكم. والصورة التي لا تعيد إرسال image_path الخاص بها تُحذف.

curl -X POST 'https://api.dzbuild.app/v1/products/26/offers' \
-H "Authorization: Bearer $DZ_KEY" -H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"offers": [
{ "title": "Buy 2, get 1 free", "quantity": 3, "price": 2000,
"compare_price": 3000, "badge_text": "Best value", "free_shipping": true },
{ "title": "Pack of 2", "quantity": 2, "discount_type": "percent", "discount_value": 10 }
]
}'

تُرجع 200 مع العروض الجديدة، بنفس شكل GET أعلاه. والحمولة المرفوضة تترك عروضك الحالية دون أي مساس.

الأخطاء​

الكودHTTPالسبب
validation_error422offers غائبة أو ليست قائمة، أو أكثر من 50 عرضًا، أو title غائب، أو قيمة غير صالحة في quantity أو price أو discount_type أو discount_value أو compare_price أو badge_color
duplicate_offer_quantity422عرضان بنفس quantity
invalid_image_path422image_path ليس صورة تستعملها عروض هذا المنتج حاليًا
not_found404المنتج ليس في متجرك

GET /v1/products/{id}/addons​

يقرأ حقول إدخال الزبون للمنتج: حقول إضافية يملؤها الزبون في صفحة المنتج (نص قصير أو نص طويل أو رفع صورة)، ومفتاح enabled الذي يُظهرها أو يُخفيها.

المصادقة: مفتاح منصة بصلاحية products:read.

curl https://api.dzbuild.app/v1/products/26/addons \
-H "Authorization: Bearer $DZ_KEY"
{
"data": {
"product_id": 26,
"enabled": true,
"addons": [
{ "id": 7, "title": "Name to print", "input_type": "text",
"placeholder": "Up to 20 letters", "is_required": true, "extra_price": 300,
"max_length": 20, "allowed_extensions": null, "sort_order": 0, "is_active": true },
{ "id": 8, "title": "Your photo", "input_type": "image",
"placeholder": null, "is_required": false, "extra_price": 0,
"max_length": null, "allowed_extensions": "jpg,png", "sort_order": 1, "is_active": true }
]
}
}

لا تُظهر صفحة المنتج الحقول إلا ما دام enabled يساوي true، ولا تُظهر إلا الحقول التي تحمل "is_active": true. ويُضاف extra_price إلى الطلب عندما يملأ الزبون ذلك الحقل.

POST /v1/products/{id}/addons​

يستبدل حقول إدخال الزبون للمنتج، ويمكنه ضبط مفتاح enabled في النداء نفسه. المصادقة: مفتاح منصة بصلاحية products:write. يتطلب Idempotency-Key.

هذا يستبدل كل حقول الإدخال في المنتج

أرسل كل حقل تريد الاحتفاظ به، بالترتيب الذي تظهر به الحقول. كل ما تحذفه من الحمولة يُحذف فعليًا. أرسل {"addons": []} لإزالة كل الحقول.

الجسم​

الحقلالنوعإلزاميملاحظات
enabledbool | nulltrue يُظهر الحقول في صفحة المنتج، وfalse يُخفيها. غيابه أو null يترك المفتاح على حاله.
addonsarray (من 0 إلى 20)✅الحقول بالترتيب الذي تظهر به. و[] تزيل كل الحقول.
addons[].titlestring✅يُقصّ إلى 255 حرفًا
addons[].input_typetext | textarea | imageالافتراضي text. وtextarea نص طويل، وimage رفع صورة.
addons[].placeholderstringيُقصّ إلى 255 حرفًا
addons[].is_requiredboolالافتراضي false
addons[].extra_pricenumber ≥ 0مبلغ بالدينار يُضاف عندما يملأ الزبون الحقل. الافتراضي 0.
addons[].max_lengthint ≥ 1لحقول text وtextarea فقط. حدّه الأقصى 65535.
addons[].allowed_extensionsقائمة أو نص مفصول بفواصللحقول image فقط: أيّ من jpg وjpeg وpng وgif وwebp. الافتراضي الخمسة كلها.
addons[].is_activeboolالافتراضي true
curl -X POST 'https://api.dzbuild.app/v1/products/26/addons' \
-H "Authorization: Bearer $DZ_KEY" -H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"enabled": true,
"addons": [
{ "title": "Name to print", "placeholder": "Up to 20 letters",
"is_required": true, "extra_price": 300, "max_length": 20 },
{ "title": "Your photo", "input_type": "image", "allowed_extensions": ["jpg", "png"] }
]
}'

تُرجع 200 مع الحقول الجديدة والمفتاح، بنفس شكل GET أعلاه. والحمولة المرفوضة تترك حقولك الحالية دون أي مساس.

الأخطاء​

الكودHTTPالسبب
validation_error422addons غائبة أو ليست قائمة، أو أكثر من 20 حقلًا، أو title غائب، أو input_type غير معروف، أو قيمة غير صالحة في extra_price أو max_length، أو max_length على حقل image، أو allowed_extensions على نوع حقل آخر أو بامتداد غير مسموح
not_found404المنتج ليس في متجرك

GET /v1/products/{id}/quantity-rules​

يقرأ الحد الأدنى والأقصى لكمية هذا المنتج في الطلب الواحد. و0 تعني بلا حد.

المصادقة: مفتاح منصة بصلاحية products:read.

curl https://api.dzbuild.app/v1/products/26/quantity-rules \
-H "Authorization: Bearer $DZ_KEY"
{
"data": {
"product_id": 26,
"min_qty": 2,
"max_qty": 10,
"has_rule": true,
"addon_id": "min-max-quantity",
"addon_active": false,
"warning": "The \"min-max-quantity\" addon is not active for this store, so this rule is stored but NOT enforced at checkout. Activate it from the dashboard Addons page."
}
}

لا تُطبَّق القاعدة عند إرسال الطلب إلا ما دامت إضافة الحد الأدنى والأقصى للكمية لكل منتج مفعّلة في المتجر. والإضافة متاحة في كل الخطط. يخبرك addon_active إن كانت مفعّلة، ويظهر warning عندما تُحفظ قاعدة والإضافة معطّلة.

POST /v1/products/{id}/quantity-rules​

يضبط الحد الأدنى والأقصى لكمية طلب المنتج. المصادقة: مفتاح منصة بصلاحية products:write. يتطلب Idempotency-Key.

الجسم​

الحقلالنوعإلزاميملاحظات
min_qtyint، من 0 إلى 100000 = بلا حد أدنى. غيابه يُحسب 0.
max_qtyint، من 0 إلى 100000 = بلا حد أقصى. غيابه يُحسب 0. وإن كان أكبر من 0 فيجب ألا يقل عن min_qty.

كل نداء يكتب القيمتين معًا، لذا أرسلهما سويًا: الحقل الذي تتركه يصير 0. وإرسال 0 للاثنين يحذف القاعدة.

curl -X POST 'https://api.dzbuild.app/v1/products/26/quantity-rules' \
-H "Authorization: Bearer $DZ_KEY" -H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{ "min_qty": 2, "max_qty": 10 }'

تُرجع 200 مع القاعدة المحفوظة، بنفس شكل GET أعلاه.

الأخطاء​

الكودHTTPالسبب
validation_error422قيمة ليست عددًا صحيحًا، أو أقل من 0، أو أكبر من 10000، أو max_qty أصغر من min_qty (قاعدة كهذه تمنع كل طلبات المنتج)
not_found404المنتج ليس في متجرك
هذه الصفحة لأدوات الذكاء الاصطناعيعرض بصيغة Markdownفتح في ChatGPTفتح في Claude