صفحات الهبوط
صفحة الهبوط صفحة تحويل مركّزة لمنتج واحد. مستقلة عن كتالوج الواجهة — يمكنك امتلاك صفحة هبوط بدون منتج حي (لإطلاقات قادمة)، أو واحدة مرتبطة بمنتج لإعلانات مدفوعة.
يمكن بناء الأقسام (السلايدرات، نماذج الطلب، الزوار الوهميون، العدّاد التنازلي…) عبر الواجهة البرمجية بنقاط الأقسام المشروحة أدناه، أو من لوحة التحكم.
حدود الخطة
| الخطة | صفحات الهبوط (كل الحالات — المسودات محسوبة) |
|---|---|
| Free | 0 (شراء لمرة واحدة: 1000 دج/مدى الحياة لكل منها) |
| Pro | 3 |
| Unlimited / Enterprise | غير محدود |
يُطبَّق الحد على كل طرق الإنشاء، بما فيها الواجهة البرمجية، مع احتساب كل صفحة هبوط بما فيها المسودات. على متجر بلغ حدّه، يُرجع POST /v1/landing-pages (وPOST /v1/landing-pages/generate) الخطأ 403 limit_reached. ولا يستطيع متجر على الخطة المجانية إنشاء صفحة إلا ما دام لديه شراء صفحة هبوط لم يُستعمل بعد؛ ويُعلِّم POST /v1/landing-pages تلك الصفحة بـ is_purchased: true، وهذا ما يجعلها مرئية على واجهة المتجر.
GET /v1/landing-pages
اسرد صفحات الهبوط. ترقيم بالمؤشّر. تُخدَم حديثًا في كل نداء، مثل GET /v1/landing-pages/{id}.
المصادقة: مفتاح منصة بصلاحية landing_pages:read، وتحتاجها GET /v1/landing-pages/{id} أيضًا. المفتاح الذي لا يحملها يتلقى 403 forbidden.
معاملات الاستعلام
| المعامل | النوع | ملاحظات |
|---|---|---|
limit | int 1–200 | الافتراضي 50 |
cursor | string | غير شفاف |
status | active | draft | تصفية |
القيمة غير المعروفة في status تُتجاهَل فتُرجَع كل الصفحات بدل 400.
الاستجابة 200
{
"data": {
"items": [
{
"id": 42,
"title": "Black T-Shirt — 30% off",
"slug": "black-tshirt-30-off",
"public_url": "https://your-store.example.com/landing/black-tshirt-30-off",
"status": "active",
"language": "ar",
"product_id": 26,
"views": 1543,
"is_purchased": false,
"created_at": "2026-03-01 10:00:00",
"updated_at": "2026-03-15 14:22:11"
}
],
"next_cursor": null,
"has_more": false
}
}
GET /v1/landing-pages/{id}
تفاصيل مع section_count.
{
"data": {
"id": 42,
"title": "Black T-Shirt — 30% off",
"slug": "black-tshirt-30-off",
"public_url": "https://your-store.example.com/landing/black-tshirt-30-off",
"status": "active",
"language": "ar",
"product_id": 26,
"views": 1543,
"is_purchased": false,
"meta_title": "Black T-Shirt — Cotton 200gsm — 30% off | DZBuild",
"meta_description": "Limited-time offer on our cotton black t-shirt.",
"section_count": 7,
"created_at": "2026-03-01 10:00:00",
"updated_at": "2026-03-15 14:22:11"
}
}
مرجع الحقول
| الحقل | ملاحظات |
|---|---|
status | active أو draft فقط بصرامة — لا توجد حالة archived لصفحات الهبوط. |
language | ar أو fr أو en. |
public_url | العنوان الحي للصفحة: عنوان المتجر (نطاقه المخصّص متى صار فعّالًا، وإلا النطاق الفرعي)، ثم /landing/ ثم الـ slug. يكون null ما دام المتجر بلا عنوان. استعمله كما هو بدل بناء الرابط بنفسك. |
product_id | المنتج المرتبط، أو null. القسم الذي يستقبل الطلبات (order_form وorder_button وproduct_offers) يحتاج إليه أو إلى settings.product_id الخاص به. بعد ضبطه، يمكن لـ PATCH تغييره إلى منتج آخر لكن لا يمكن إرجاعه إلى null. |
views | للقراءة فقط. تُحتسب في كل مرة تُشاهَد فيها الصفحة العامة؛ ولا تستطيع الواجهة البرمجية كتابتها ولا توجد طريقة لتصفيرها. |
is_purchased | true بمجرد شراء الصفحة نهائيًا (1000 دج/مدى الحياة). على الخطة المجانية هذا ما يجعل الصفحة مرئية على واجهة المتجر. |
section_count | في نقطة التفاصيل فقط — عدّ حيّ لأقسام الصفحة يُحسب مع كل طلب. |
meta_title / meta_description | وسوم SEO. انظر الملاحظة تحت الإنشاء. |
POST /v1/landing-pages — إنشاء
المصادقة: مفتاح منصة بصلاحيتَي landing_pages:write وlanding_pages:read. تقرأ الاستجابة الصفحة بعد حفظها، فمع landing_pages:write وحدها تُحفَظ الصفحة ويُرجع النداء 403 forbidden؛ والأمر نفسه مع PATCH و/publish. يتطلب Idempotency-Key.
الجسم
| الحقل | النوع | إلزامي | ملاحظات |
|---|---|---|---|
title | string، من 1 إلى 255 بايت | ✅ | الحد يحسب البايتات لا الحروف: الحرف العربي يأخذ 2 بايت، فالعنوان العربي يتوقف عند نحو 127 حرفًا |
slug | string | يُشتق تلقائيًا من title إن أُغفل. أما الـ slug الذي ترسله هنا فيُخزَّن بدون تطبيع — أرسل قيمة نظيفة | |
status | active | draft | الافتراضي draft. أي قيمة أخرى تُحوَّل بصمت إلى draft | |
language | ar | fr | en | الافتراضي ar. أي قيمة أخرى تُحوَّل بصمت إلى ar | |
product_id | int | يجب أن ينتمي إلى متجرك؛ الصفحة تربط بهذا المنتج | |
meta_title | string ≤ 255 | عنوان SEO. إن أغفلته عبر الواجهة البرمجية يُخزَّن ويُرجَع كـ null (بخلاف نموذج لوحة التحكم الذي ينسخ title إليه). ومع ذلك تعرض الصفحة العامة title كبديل، فعنوان الصفحة الظاهر صحيح في الحالتين | |
meta_description | string | وصف SEO |
تُجعل الـ slugs فريدة داخل متجرك بإلحاق -2 و-3 وهكذا. وإن كان أساس الـ slug فارغًا فيُستعمل landing- متبوعًا بـ 6 أحرف hex.
الأخطاء
| الكود | السبب |
|---|---|
bad_request "Body must be valid JSON" | Content-Type خاطئ أو JSON تالف |
bad_request "title is required (1-255 chars)" | العنوان مفقود أو طويل جدًا |
bad_request "product_id N does not belong to this store" | معرّف من متجر آخر |
limit_reached (403) | بلغ المتجر حد صفحات الهبوط في خطته (المسودات محسوبة). على الخطة المجانية: لم يبقَ شراء صفحة هبوط غير مستعمل |
الطلب
curl -X POST 'https://api.dzbuild.app/v1/landing-pages' \
-H "Authorization: Bearer $DZ_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"title": "Black T-Shirt — 30% off",
"language": "ar",
"product_id": 26,
"status": "draft"
}'
تُرجع 200 (وليس 201) وبنفس شكل GET /v1/landing-pages/{id}. صفحة الهبوط الجديدة بدون أقسام: أضفها بنقاط الأقسام المشروحة أدناه. فحص النشر لا يجري عند الإنشاء، لذا أنشئ الصفحة كـ draft وانشرها بعد إضافة أقسامها؛ فالصفحة المُنشأة بـ status: active تظهر للزبائن فارغة.
PATCH /v1/landing-pages/{id}
تحديث جزئي.
يتحقّق PATCH بصرامة أكبر من الإنشاء: status غير صالح يُرجع 400 bad_request ("status must be active or draft")، وlanguage غير صالح يُرجع 400 ("language must be ar, fr, or en") بدل التحويل الصامت. ويجب أن يبقى title بين 1 و255 بايت. أما الـ slug المُرسَل في PATCH فيُطبَّع، بخلاف الإنشاء. ضبط status على active يُشغّل فحص النشر (انظر /publish أدناه). وبعد أن يصير للصفحة منتج، يُرجع product_id: null الخطأ 422 product_required؛ أرسل معرّف منتج آخر لتغيير المنتج.
curl -X PATCH 'https://api.dzbuild.app/v1/landing-pages/42' \
-H "Authorization: Bearer $DZ_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{ "title": "Black T-Shirt — Spring promo" }'
تغيير العنوان يُعيد توليد slug تلقائيًا فقط إن لم تُرسل slug صراحةً. وعندما يتغيّر الـ slug، بتغيير العنوان أو صراحةً، يبقى الرابط القديم يعمل ويُحوِّل إلى الرابط الجديد.
الأقسام
تعرض الصفحة أقسامها من الأعلى إلى الأسفل. قراءة الأقسام تحتاج إلى landing_pages:read، وكتابتها تحتاج إلى landing_pages:write، وكل كتابة تحتاج إلى Idempotency-Key.
| النقطة | الجسم | الاستجابة |
|---|---|---|
GET /v1/landing-page-section-types | 200: section_types، كل عنصر فيها type مع default_settings الخاصة به | |
GET /v1/landing-pages/{id}/sections | 200: landing_page_id وsections بترتيب العرض (حتى 500، بدون ترقيم) | |
POST /v1/landing-pages/{id}/sections | section_type (أو type)، وsettings اختياري | 201: الـ section الجديد، مُضافًا في آخر الصفحة |
POST /v1/landing-pages/{id}/sections/batch | sections: حتى 30 كائنًا فيها type (أو section_type) وsettings اختياري | 201: الـ sections الجديدة. الكل أو لا شيء: عنصر واحد خاطئ يمنع حفظ الجميع |
POST /v1/landing-pages/{id}/sections/reorder | sections: كل معرّفات أقسام الصفحة، كل واحد مرة واحدة، بالترتيب الجديد | 200: الأقسام بترتيبها الجديد |
PATCH /v1/landing-pages/{id}/sections/{section_id} | settings، وreplace اختياري | 200: الـ section بعد التحديث |
DELETE /v1/landing-pages/{id}/sections/{section_id} | 200: { "deleted": true, "id": 901, "landing_page_id": 42 } |
يحمل القسم id وsection_type وsort_order وsettings. والاستجابات التي تعيد قراءة الصفحة (القائمة وإعادة الترتيب وPATCH) تحمل أيضًا created_at وupdated_at. الأنواع الـ 14 هي image وorder_form وorder_button وfree_text وcontact_button وcountdown وfake_visitors وspecial_offer وprice_display وproduct_offers وcustom_form وimage_carousel وannouncement_bar وtestimonials. اقرأ GET /v1/landing-page-section-types قبل الكتابة، حتى تطابق مفاتيح إعداداتك ما تعرضه الصفحة.
- الإعدادات التي ترسلها تُدمج فوق القيم الافتراضية للنوع، فنداء واحد يكفي لإنشاء قسم مضبوط بالكامل. ومع
PATCHتُدمج فوق الإعدادات المخزّنة؛ أرسل"replace": trueلتبدأ من جديد من القيم الافتراضية للنوع. الكائنات المتداخلة تُدمج مفتاحًا بمفتاح، أما القوائم مثلslidesوitemsوoffersوfieldsفتُستبدل كاملة. ولا يمكن تغيير نوع القسم. - قسم
order_formأوorder_buttonأوproduct_offersيحتاج إلى منتج:product_idالخاص بالصفحة أوsettings.product_idالخاص به. بدونه يُرجع النداء422 landing_page_has_no_product. وsettings.product_idمن متجر آخر يُرجع422 validation_error. - حقول نموذج الطلب
show_nameوshow_phoneوshow_wilayaتُحفَظ دائمًا بقيمةtrueفي أي قسم يحملها. - قائمة
slidesتقبل 20 عنصرًا على الأكثر، وقائمةitemsتقبل 30 على الأكثر (422 too_many_items). ولا يجوز أن تتجاوز الإعدادات المُرمَّزة 262144 بايت (422 settings_too_large). والنوع غير المعروف يُرجع422 invalid_section_type. - عند الكتابة، الصفحة التي ليست من متجرك تُرجع
404 landing_page_not_found(القائمة والفحص يُرجعان404 not_found)، والقسم غير الموجود على الصفحة يُرجع404 section_not_found. وقائمة إعادة الترتيب التي تكرّر قسمًا أو تُغفله تُرجع422 validation_error. - تظهر تغييرات الأقسام في
GET /v1/changes. يمكن التراجع عن التعديل والحذف وإعادة الترتيب بـPOST /v1/changes/{id}/undo، والقسم المحذوف الذي يعود بالتراجع يأخذ رقمًا جديدًا. أما إضافة قسم فلا يمكن التراجع عنها: احذفه بدل ذلك.
curl -X POST 'https://api.dzbuild.app/v1/landing-pages/42/sections/batch' \
-H "Authorization: Bearer $DZ_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"sections": [
{ "type": "announcement_bar" },
{ "type": "order_form" }
]
}'
GET /v1/landing-pages/{id}/check
يُبلغ عمّا سيجده الزبون معطّلًا في الصفحة. المصادقة: landing_pages:read.
تحمل الاستجابة landing_page_id وstatus وproduct_id وsections (عدد الأقسام المفحوصة) وpublishable وblocked_by (أول رسالة مانعة، أو null) وproblems. لكل مشكلة code وseverity (blocking أو warning) وmessage؛ والمشاكل المرتبطة بقسم واحد تحمل أيضًا section_id.
| الكود | الخطورة | المعنى |
|---|---|---|
empty_page | blocking | الصفحة بلا أقسام، فتظهر فارغة |
order_form_without_product | blocking | قسم يستقبل الطلبات، لكن لا الصفحة ولا القسم يحدّدان منتجًا، فتُسجَّل الطلبات بـ 0 دج |
section_product_not_found | blocking | قسم يشير إلى منتج ليس في متجرك |
no_order_form | warning | لا شيء في الصفحة يستقبل الطلبات |
multiple_order_forms | warning | يظهر أكثر من order_form أو order_button واحد |
variants_need_page_product | warning | قسم يحدّد منتجًا بمتغيرات والصفحة بلا منتج. أدوات اختيار المتغيرات لا تظهر إلا من منتج الصفحة نفسه، فاضبط product_id على الصفحة |
يُرفض النشر ما دامت هناك مشكلة مانعة (انظر أدناه).
POST /v1/landing-pages/{id}/publish
اختصار: تحويل الحالة إلى active. يكافئ PATCH ... { status: "active" }، ويُرفض بالطريقة نفسها: ما دام GET /v1/landing-pages/{id}/check يُبلغ عن مشكلة مانعة، يُرجع النداء 422 page_not_publishable ويحمل الخطأ قائمة problems. أما التعديلات التي لا ترسل status فلا تمرّ بهذا الفحص، حتى على صفحة منشورة.
curl -X POST 'https://api.dzbuild.app/v1/landing-pages/42/publish' \
-H "Authorization: Bearer $DZ_KEY" \
-H "Idempotency-Key: publish-42-$(date +%s)"
POST /v1/landing-pages/generate
يبني صفحة هبوط من أحد منتجاتك بالذكاء الاصطناعي. يُرجع 202 فورًا مع مهمة تتابعها؛ وتستغرق العملية نحو دقيقتين. المصادقة: صلاحية ai:generate. المفاتيح المُنشأة من لوحة التحكم أو عبر POST /v1/keys والتطبيقات الخارجية لا تحملها وتتلقى 403 forbidden؛ أما اتصالات Claude وChatGPT وDZBuild Copilot فتحملها. يتطلب Idempotency-Key.
| الحقل | النوع | إلزامي | ملاحظات |
|---|---|---|---|
title | string | ✅ | 3 أحرف على الأقل، ويُقطع عند 255 |
product_id | int | ✅ | منتج نشِط من متجرك فيه صورة واحدة على الأقل |
description | string | وصف موجز للصفحة، يُقطع عند 2000 حرف | |
language | ar | fr | en | الافتراضي ar | |
size | medium | tall | الافتراضي medium. تكلفة medium هي 20 نقطة ذكاء اصطناعي، وتكلفة tall هي 45 |
تحمل الاستجابة task_id وsize وcredits_charged وeta_seconds وpoll. تُخصم النقاط عند بدء العملية وتُعاد إذا فشلت أو تجاوزت المهلة. الأخطاء: 400 bad_request (العنوان مفقود أو أقصر من 3 أحرف)، 402 quota_exceeded (نقاط الذكاء الاصطناعي غير كافية)، 403 limit_reached (حد صفحات الهبوط في الخطة، مع limit وcurrent)، 409 already_processing (توليد واحد لكل متجر في الوقت نفسه)، 422 product_required أو product_not_found أو product_has_no_image، 429 rate_limited أو too_many_concurrent، 503 provider_unavailable (بدون خصم أي نقاط).
تابِع GET /v1/landing-pages/generate/{task_id} بصلاحية landing_pages:read. تُرجع task_id وstatus (processing أثناء بناء الصفحة، ثم completed أو failed) وlanding_page_id بمجرد وجود الصفحة وcurrent_step وerror. العملية التي تبقى قيد المعالجة بعد 10 دقائق تُعلَّم failed بالخطأ timeout عند المتابعة التالية، وتُعاد نقاطها.
DELETE /v1/landing-pages/{id}
حذف نهائي. وتُحذف أقسام الصفحة معها.
curl -X DELETE 'https://api.dzbuild.app/v1/landing-pages/42' \
-H "Authorization: Bearer $DZ_KEY" \
-H "Idempotency-Key: del-42"
الاستجابة: { "data": { "deleted": true, "id": 42 } }.