التغييرات والتراجع عنها
أغلب كتابات الإعدادات التي تمر عبر الواجهة البرمجية تُسجَّل كتغييرات: إعدادات المتجر وتصميمه وقالبه، وأقسام الصفحة الرئيسية، والفئات، والمخزون، وأكواد الخصم، والبيكسلات، وأسعار الشحن وإعدادات التوصيل، وأقسام صفحات الهبوط. يحتفظ كل تغيير بالقيم التي استبدلها، والتراجع يعيد كتابتها عبر الفحوص نفسها التي مرّت بها الكتابة الأصلية. الكتابات التي يُجريها على المتجر Copilot أو مساعد ذكاء اصطناعي متصل (Claude أو ChatGPT) أو تطبيق مثبّت تُسجَّل هي أيضاً. أما التغييرات المحفوظة من لوحة التحكم فلا تُسجَّل.
النقاط الثلاث أدناه تعرض قائمة التغييرات، وتقرأ تغييراً واحداً كاملاً، وتتراجع عن تغيير. الكتابات تحت /v1/store/home-layout ومزامنة الأسعار من شركة التوصيل تُرجع change_id. أما بقية الكتابات فابحث عن تغييرها عبر GET /v1/changes.
ما الذي يُسجَّل
entity | تُسجّله | entity_id | الصلاحية لقراءته كاملاً | الصلاحية للتراجع عنه |
|---|---|---|---|---|
store.settings | PATCH /v1/store | settings | store:read | store:write |
store.design | PATCH /v1/store/design | design | store:read | store:write |
store.theme | POST /v1/store/theme وPOST /v1/store/fast-checkout-theme وPOST /v1/store/variant-style | theme | store:read | store:write |
store.home_sections | PATCH /v1/store/home-sections | home_sections | store:read | store:write |
store.home_layout | كل كتابة تحت /v1/store/home-layout | layout | store:read | store:write |
category | POST /v1/categories وPATCH وDELETE /v1/categories/{id} | رقم الفئة | products:read | products:write |
stock | POST /v1/products/{id}/stock | رقم المنتج | products:read | products:write |
promo_code | POST /v1/promo-codes وPATCH وDELETE /v1/promo-codes/{id} | رقم كود الخصم | promos:read | promos:write |
pixels | POST /v1/pixels وPATCH وDELETE /v1/pixels/{id} | رقم البيكسل في DZBuild، وليس pixel_id | pixels:read | pixels:write |
shipping.rates | POST /v1/shipping/rates وPOST /v1/shipping/rates/sync | rates | shipping:read | shipping:write |
shipping.settings | PATCH /v1/shipping/settings | settings | shipping:read | shipping:write |
lp.section | كتابات الأقسام تحت /v1/landing-pages/{id}/sections | رقم القسم، أو lp: متبوعة برقم الصفحة عند إعادة الترتيب | landing_pages:read | landing_pages:write |
- هذه الكتابات لا تُسجَّل ولا يمكن التراجع عنها: المنتجات مع صورها ومتغيراتها وعروضها وإضافاتها وقواعد الكمية (المخزون يُسجَّل)، والطلبات، وصفحات الهبوط نفسها، وترتيب الفئات، وشركات التوصيل، والـ
webhooks، والمفاتيح. - القيمة
lp.pageمقبولة كفلتر لـentity، لكن لا توجد كتابة تُسجّلها. - التسجيل لا يوقف الكتابة. إذا تعذّر تسجيل تغيير، مثلاً لأن قيمه قبل الكتابة أو بعدها تتجاوز 256 KB، تتم الكتابة رغم ذلك ولا يمكن التراجع عنها. كتابات أسعار الشحن هي الاستثناء: تفحص الحجم أولاً وتُرجع
422 snapshot_too_largeبدل أن تُنفَّذ. - كل صلاحية في الجدول ضمن الصلاحيات الافتراضية للمفتاح الجديد، فالمفتاح المُنشأ من لوحة التحكم (الإعدادات ← واجهة API،
/dashboard/api) يستطيع استعمال النقاط الثلاث. الصلاحيات تُجمَّد لحظة إنشاء المفتاح، فالمفتاح القديم الذي تنقصه الصلاحية المطلوبة يُرجع403 forbidden: أنشئ مفتاحاً جديداً من لوحة التحكم.
GET /v1/changes
تغييرات المتجر، الأحدث أولاً، دون القيم التي استبدلتها.
المصادقة: مفتاح منصة بصلاحية store:read. رمز التطبيق المثبّت يتلقى 403 forbidden (Apps cannot use this endpoint).
معاملات الاستعلام
| المعامل | النوع | الافتراضي | ملاحظات |
|---|---|---|---|
entity | string | لا شيء | تغييرات entity واحدة من الجدول أعلاه فقط. أي قيمة أخرى تُتجاهل وتعود القائمة كاملة. |
limit | int | 25 | من 1 إلى 100. القيمة الأصغر تُحسب 1، والأكبر تُحسب 100. |
cursor | string | لا شيء | قيمة next_cursor من الصفحة السابقة. انظر الترقيم. |
الطلب
curl 'https://api.dzbuild.app/v1/changes?limit=2' \
-H "Authorization: Bearer $DZ_KEY"
الاستجابة 200
{
"data": {
"items": [
{
"id": 120,
"entity": "shipping.rates",
"entity_id": "rates",
"action": "update",
"summary": "Shipping rates updated for 2 wilaya(s)",
"undone_at": null,
"created_at": "2026-10-06 11:02:17",
"undone": false
},
{
"id": 119,
"entity": "promo_code",
"entity_id": "7",
"action": "update",
"summary": "Updated promo code SUMMER10",
"undone_at": null,
"created_at": "2026-10-06 10:52:30",
"undone": false
}
],
"next_cursor": "MTE5",
"has_more": true
}
}
| الحقل | المعنى |
|---|---|
id | رقم التغيير، لـ GET /v1/changes/{id} وللتراجع. |
entity | ما الذي تغيّر. انظر الجدول أعلاه. |
entity_id | العنصر الذي تغيّر، كنص. انظر الجدول أعلاه. |
action | create أو update أو delete. التراجع يُسجَّل كـ update. |
summary | وصف قصير بالإنجليزية. التراجع يُكتب Undo of change # متبوعاً برقم التغيير الذي تراجع عنه. |
undone | true بعد التراجع عن التغيير. |
undone_at | وقت التراجع عن التغيير، وإلا null. |
created_at | YYYY-MM-DD HH:MM:SS بتوقيت الخادم. وundone_at بالصيغة نفسها. |
GET /v1/changes/{id}
تغيير واحد مع before، أي القيم التي استبدلها، وafter، أي القيم التي كتبها. شكلهما يتبع نوع العنصر: بعضها يحتفظ فقط بالحقول التي لمستها الكتابة، وبعضها بالعنصر كله.
المصادقة: مفتاح منصة بصلاحية store:read، إضافة إلى صلاحية القراءة الخاصة بنوع التغيير في الجدول أعلاه. رمز التطبيق المثبّت يتلقى 403 forbidden.
الطلب
curl 'https://api.dzbuild.app/v1/changes/118' \
-H "Authorization: Bearer $DZ_KEY"
الاستجابة 200
{
"data": {
"id": 118,
"key_id": "dzpk_live_xxxxxxxxxxxxxx",
"entity": "category",
"entity_id": "12",
"action": "update",
"summary": "Updated category #12 (name, slug)",
"undone_at": null,
"undone_by_id": null,
"created_at": "2026-10-06 10:41:05",
"before": {"name": "Shoes", "slug": "shoes"},
"after": {"name": "Sneakers", "slug": "sneakers"},
"undone": false
}
}
تحمل الاستجابة حقول القائمة، وهذه الحقول أيضاً.
| الحقل | المعنى |
|---|---|
key_id | المفتاح الذي أجرى التغيير، أو null لتراجع أُجري من لوحة التحكم. |
undone_by_id | رقم التغيير الذي تراجع عن هذا التغيير، وإلا null. |
before | القيم التي استبدلها التغيير. null في حالة create. |
after | القيم التي كتبها التغيير. null في حالة delete وفي مزامنة الأسعار من شركة التوصيل. |
رمز الوصول (access token) الخاص بالبيكسل لا يُحفظ أبداً في التغيير. إذا كان للبيكسل رمز، يظهر •••••••• مكانه في before وafter.
الأخطاء
| HTTP | الرمز | السبب |
|---|---|---|
| 400 | bad_request | رقم التغيير في المسار ليس أرقاماً فقط. |
| 403 | forbidden | المفتاح لا يملك store:read أو صلاحية القراءة الخاصة بنوع التغيير (Missing scope: ...)، أو النداء من رمز تطبيق مثبّت. |
| 404 | not_found | لا يوجد تغيير بهذا الرقم في المتجر. |
POST /v1/changes/{id}/undo
يعيد كتابة قيم before الخاصة بالتغيير عبر الفحوص نفسها التي مرّت بها الكتابة الأصلية، ثم يسجّل التراجع كتغيير جديد. بلا جسم للطلب.
المصادقة: مفتاح منصة بصلاحية التراجع الخاصة بنوع التغيير في الجدول أعلاه، ولا حاجة إلى store:read. يتطلب Idempotency-Key. رمز التطبيق المثبّت يتلقى 403 forbidden (Apps cannot use this endpoint). يرى مالك المتجر آخر 20 تغييراً أجراها كل تطبيق مثبّت في صفحة ذلك التطبيق في لوحة التحكم (/dashboard/apps/{id}، قسم آخر التغييرات التي أجراها)، ويستطيع التراجع عنها من هناك بزر تراجع.
ما يفعله التراجع
- التغيير الذي أنشأ شيئاً ليس له ما يُستعاد، فيُرجع
422 nothing_to_restore: احذف العنصر بدل ذلك. إضافة قسم للصفحة الرئيسية عبر/v1/store/home-layoutهي الاستثناء، لأن كل كتابة على تخطيط الصفحة الرئيسية تُسجَّل كـupdateللتخطيط كله. - التراجع عن
updateيعيد كتابة قيمbeforeفوق ما هو موجود الآن. الصفحة الرئيسية وحدها تفحص التغييرات اللاحقة وتُرجع409 layout_changed(انظر أقسام الصفحة الرئيسية). للتراجع عن عدة تغييرات على العنصر نفسه، ابدأ بالأحدث. - الفئة المحذوفة تعود برقمها، دون صورتها. وكود الخصم المحذوف يعود برقمه إذا لم يأخذه كود آخر، ومع عدد مرات استعماله.
- البيكسل المحذوف يعود برقمه إذا بقي متاحاً، لكن دون رمز الوصول ودون ربطه بالمنتجات والفئات وصفحات الهبوط. والتراجع عن تعديل بيكسل يترك رمز الوصول الحالي كما هو.
- قسم صفحة الهبوط المحذوف يعود برقم جديد.
- التراجع عن المخزون يعيد كل قيمة إلى الرقم المسجَّل قبل التغيير، مهما فعلت الطلبات بالمخزون منذ ذلك الحين.
- التراجع عن
POST /v1/shipping/ratesيعيد الأسعار السابقة، ويحذف سعر الولاية التي لم يكن لها سعر قبل التغيير. والتراجع عن مزامنة الأسعار من شركة التوصيل يعيد كل سعر استبدلته المزامنة، ويُبقي الأسعار التي أضافتها لولايات لم يكن لها سعر. - التراجع يُسجَّل تغييراً مستقلاً،
undo_change_id، ويمكنك التراجع عنه لتطبيق التغيير الأصلي من جديد. إذا لم يكن للتغيير الأصليafter(حذف أو مزامنة أسعار من شركة التوصيل)، يُرجع التراجع عن التراجع422 nothing_to_restore. - يمكن التراجع عن التغيير مرة واحدة. أي تراجع لاحق يُرجع
409 already_undone، ومن تراجعَين يُرسَلان في اللحظة نفسها لا يُنفَّذ إلا واحد. - إذا فشلت الاستعادة نفسها (
restore_target_missing، أو قيمة مرفوضة، أو500)، لا يُعلَّم التغيير كمتراجَع عنه، فيمكنك إعادة المحاولة بعد إصلاح السبب. قواعد إعادة المحاولة أسفله.
استجابة التراجع لا تحمل القيم المستعادة. اقرأ العنصر من جديد. عبر api.dzbuild.app، قد تُرجع ذاكرة القراءة المؤقتة القصيرة (GET /v1/store وكل طلب GET تحته، وقائمتا GET /v1/products وGET /v1/landing-pages) القيمَ القديمة لمدة تصل إلى 30 ثانية بعد التراجع.
الطلب
curl -X POST 'https://api.dzbuild.app/v1/changes/118/undo' \
-H "Authorization: Bearer $DZ_KEY" \
-H "Idempotency-Key: undo-118"
الاستجابة 200
{
"data": {
"undone": true,
"change_id": 118,
"entity": "category",
"undo_change_id": 121
}
}
| الحقل | المعنى |
|---|---|
undone | دائماً true مع 200. |
change_id | التغيير الذي تم التراجع عنه. |
entity | نوعه. |
undo_change_id | التغيير الذي يسجّل هذا التراجع، أو null إذا تعذّر تسجيله. |
الأخطاء
| HTTP | الرمز | السبب |
|---|---|---|
| 400 | bad_request | رقم التغيير في المسار ليس أرقاماً فقط، أو Idempotency-Key غائب أو بصيغة خاطئة. |
| 403 | forbidden | المفتاح لا يملك صلاحية التراجع الخاصة بنوع التغيير (Missing scope: ...)، أو النداء من رمز تطبيق مثبّت. |
| 404 | not_found | لا يوجد تغيير بهذا الرقم في المتجر. |
| 409 | already_undone | سبق التراجع عن التغيير. |
| 409 | layout_changed | الصفحة الرئيسية فقط: تغيّر التخطيط بعد هذا التغيير. |
| 422 | not_undoable | هذا النوع من التغييرات لا يمكن التراجع عنه. |
| 422 | nothing_to_restore | التغيير أنشأ شيئاً، أو لا يحمل قيماً تُستعاد. احذف العنصر بدل ذلك. |
| 422 | restore_target_missing | ما لمسه التغيير لم يعد موجوداً، مثلاً فئة أو قسم صفحة هبوط حُذف منذ ذلك الحين. |
| 422 | idempotency_key_reuse | استُعمل Idempotency-Key نفسه من قبل لطلب مختلف، مثل التراجع عن تغيير آخر. |
| 4xx | رمز الكتابة الأصلية | فحوص الكتابة الأصلية ترفض القيم، مثلاً invalid_wilaya في أسعار الشحن، أو قالب لم يعد متاحاً. |
| 500 | server_error | تعذّر تنفيذ التراجع. يبقى التغيير قابلاً للتراجع عنه. |
إعادة المحاولة وIdempotency-Key
أول استجابة لكل مفتاح تُحفظ 24 ساعة، بما فيها استجابات 4xx. إعادة المحاولة بالمفتاح نفسه للتغيير نفسه تُرجع تلك الاستجابة مع Idempotency-Replay: 1، ولا يُنفَّذ تراجع ثانٍ. لذلك بعد إصلاح سبب الخطأ، أعد المحاولة بمفتاح جديد. استجابات 5xx و429 لا تُحفظ أبداً، فأعد المحاولة بالمفتاح نفسه. انظر Idempotency.