لا يفرض API v1 حدًّا لعدد webhooks لكل متجر — سجّل ما يحتاجه تكاملك فعليًا من روابط، ونظّف ما توقفت عن استعماله. (أما إضافة Webhooks للتاجر بدون كود فتفرض حدًّا: نقطة واحدة في Unlimited، و3 في Enterprise.)
كل webhook يمكن أن يشترك بقائمة مختلفة من الأحداث؛ اختر النموذج المناسب لك:
نقطة واحدة، كل الأحداث — أسهل للتطبيقات الصغيرة. تفرّع على
eventفي معالجك.عدة نقاط، حدث واحد لكل — أنظف في إعدادات microservices، لكن روابط أكثر للإدارة.
⚠️ تنبيه — الوصول التجريبي (Pilot)
API v1 مُقيَّد بالبرنامج التجريبي في الإنتاج. لا يكفي أن يكون المفتاح صالحًا — يجب أن تُسجّله DZBuild في البرنامج التجريبي، وإلا فكل نداء يُرجع 403 forbidden "API is in pilot mode; key not enrolled".
POST /v1/webhooks — تسجيل
المصادقة: مفتاح منصة بصلاحية webhooks:write. يتطلب Idempotency-Key.
الجسم
الحقل | النوع | إلزامي | ملاحظات |
| string (https URL) | ✅ | يجب أن يكون |
| string[] | ✅ | قائمة أسماء أحداث. انظر كتالوج الأحداث للقيم المسموح بها. مصفوفة فارغة = خطأ. |
الطلب
curl -X POST 'https://api.dzbuild.app/v1/webhooks' \
-H "Authorization: Bearer $DZ_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"url": "https://yourapp.example/webhooks/dzbuild",
"events": ["order.created", "order.confirmed", "order.shipped",
"order.cancelled", "signup.counted"]
}'
الاستجابة 200
{
"data": {
"id": 17,
"secret": "fc9b5f0b51b4a93c1d6f8e29b6a2e30c7c2c44a4f2a6c8d8e0e1b9d4f6c1a8b3",
"note": "Save the secret now — it is not retrievable after this response."
},
"meta": { "request_id": "...", "api_version": "v1" }
}
💡 نصيحة — لا تتفرّع أبدًا على == 201
لا توجد نقطة إنشاء في v1 تُرجع 201 اليوم — /v1/webhooks و /v1/products و /v1/orders و /v1/landing-pages و /v1/keys كلها تُجيب بـ 200 عند النجاح. تفرّع على أي 2xx بدلًا من ذلك.
secret يظهر مرة واحدة. احفظه بجانب معرّف الـ webhook في مدير الأسرار. لاحظ أنه ليس حاليًا المفتاح الذي تُوقَّع به تسليمات API v1 — انظر التوقيع — فهو اليوم قيمة تحتفظ بها للمستقبل، لا قيمة تتحقق بها. إن فقدته، احذف الـ webhook وأنشئ غيره.
الأخطاء
HTTP | الكود / الرسالة | السبب |
400 |
| شكل رابط خاطئ |
400 |
| مصفوفة فارغة |
400 |
| اسم حدث غير موجود في الكتالوج |
400 |
| نسيان |
400 |
| المفتاح أطول من اللازم، أو يحوي محارف خارج تلك المجموعة (محارف base64 وهي |
403 |
| المفتاح موجود لكنه غير مُسجَّل في البرنامج التجريبي |
500 |
| غالبًا |
GET /v1/webhooks — قائمة
المصادقة: مفتاح منصة بصلاحية webhooks:read.
curl https://api.dzbuild.app/v1/webhooks \ -H "Authorization: Bearer $DZ_KEY"
{
"data": {
"items": [
{
"id": 17,
"url": "https://yourapp.example/webhooks/dzbuild",
"events": ["order.created", "order.confirmed"],
"status": "active",
"last_success_at": "2026-04-30 21:18:23",
"last_failure_at": null,
"failure_count": 0,
"created_at": "2026-04-30 19:00:00"
}
],
"allowed_events": [
"order.created", "order.confirmed", "order.shipped", "order.delivered",
"order.cancelled", "order.returned", "payment.received",
"signup.counted", "event.recorded", "product.stock_low"
]
}
}
allowed_events هي القائمة التي يقبلها الـ API عند التسجيل — لكنها أوسع مما يُطلق فعليًا. فـ payment.received و event.recorded و product.stock_low تُقبل ثم لا تُصدَر أبدًا. راجع كتالوج الأحداث قبل أن تبني على أي منها.
الحالة | المعنى |
| يستلم التسليمات. عمليًا هذه هي القيمة الوحيدة التي ستراها. |
| محجوزة للاستعمال مستقبلًا، غير مستعملة حاليًا — ولا توجد صفحة في لوحة التحكم لـ webhooks الخاصة بـ API v1. |
| محجوزة للاستعمال مستقبلًا، غير مستعملة حاليًا. لا يوجد تعطيل تلقائي؛ يواصل |
لإيقاف التسليمات، احذف الـ webhook.
POST /v1/webhooks/{id}/test
أطلق تسليم webhook.test لتتحقق من أن نقطتك قابلة للوصول ولترى شكل المظروف.
لا يمكن الاشتراك في webhook.test: وضعه في مصفوفة events عند التسجيل يُرجع 400 bad_request "unknown event: webhook.test. Allowed: …". ويُرسَل تسليم الاختبار إلى الـ webhook المستهدف بغض النظر عمّا يشترك فيه ذلك الـ webhook.
المصادقة: مفتاح منصة بصلاحية webhooks:write. يتطلب Idempotency-Key.
curl -X POST 'https://api.dzbuild.app/v1/webhooks/17/test' \ -H "Authorization: Bearer $DZ_KEY" \ -H "Idempotency-Key: test-17-$(date +%s)"
{ "data": { "tested": true, "note": "a webhook.test delivery was enqueued; check your endpoint" } }
تسليم الاختبار يبدو هكذا:
{
"event": "webhook.test",
"store_id": 13,
"occurred_at": "2026-04-30T21:24:17+00:00",
"data": { "ts": 1717112657 },
"delivery_id": "9f2c41ab77e05d18"
}
أمران يجب معرفتهما عنه:
يُوضع في طابور، وليس متزامنًا. الاستجابة تؤكد فقط أن التسليم دخل الطابور؛ ويصل الـ POST بعد ذلك بقليل، خلال دقيقة عادةً.
لا يمكنه التحقق من صحة شيفرة التحقق لديك. كأي تسليم في API v1، توقيعه ليس مشتقًّا من
secretالخاص بـ webhook لديك — فهو يثبت إمكانية الوصول وشكل الحمولة، لا أكثر. انظر التوقيع.
DELETE /v1/webhooks/{id}
المصادقة: مفتاح منصة بصلاحية webhooks:write. يتطلب Idempotency-Key.
curl -X DELETE 'https://api.dzbuild.app/v1/webhooks/17' \ -H "Authorization: Bearer $DZ_KEY" \ -H "Idempotency-Key: del-17"
{ "data": { "deleted": true, "id": 17 } }
بعد الحذف: - لا تُجدوَل تسليمات جديدة. - كل تسليم في الطابور لذلك الـ webhook يُلغى فورًا. لا شيء "يُحاوَل مرة أخيرة". إن كان المتراكم يهمك، أوقف كتاباتك ودع الطابور يفرغ (أقل من دقيقة عادةً) قبل الحذف. - سر الـ webhook صار عديم الفائدة.
إعادة تشغيل Idempotency-Key في test و DELETE
يُخزّن الـ API استجابة كل Idempotency-Key لمدة 24 ساعة ويعيد تشغيلها مع ترويسة Idempotency-Replay: 1. لذلك نتيجتان:
إعادة استعمال مفتاح حرفي مثل
del-17خلال 24 ساعة تعيد الاستجابة المخزّنة بدل تنفيذ نداء جديد. استعمل مفتاحًا جديدًا (أو مفتاحًا يتضمّن رقم المحاولة) كلما أردت فعلًا أن تُنفَّذ العملية.استجابات الخطأ تُخزَّن أيضًا. الكتابة الفاشلة تعيد نفس الـ 4xx لمدة 24 ساعة تحت نفس المفتاح — غيّر المفتاح بعد إصلاح الطلب.
إعادة التشغيل مضمونة لمدة 24 ساعة أيًّا كان المضيف الذي تناديه، وتُجيب دائمًا بترويسة Idempotency-Replay: 1. ومع ذلك يُفضَّل استعمال https://api.dzbuild.app/v1: فالمسار dzbuild.com/api/v1 مجرد اسم بديل، ولا يحصل على ذاكرة القراءة المؤقتة لمدة 30 ثانية، وقد تُحجب عليه بعض المسارات.
متطلبات النقطة
رابط webhook يجب أن:
يردّ بـ 2xx عند النجاح. الأخطاء 4xx و 3xx إخفاقات بمحاولة واحدة؛ أما 5xx فيُعاد إرسال POST كل دقيقة حتى يتوقف (انظر سلوك إعادة المحاولة).
يجيب خلال 10 ثوانٍ إجمالًا (و5 ثوانٍ للاتصال). الأبطأ يُحسب timeout، والـ timeout لا يُعاد — يُهجر التسليم.
يقدّم TLS صالحًا. الشهادات تُتحقَّق بصرامة، فالشهادات الموقّعة ذاتيًا تفشل بلا إعادة.
لا يُعيد التوجيه. لا نتبع
Location؛ و301/302إخفاق.يقبل
POSTمعContent-Type: application/json.يقرأ الجسم الخام إن كنت تُجزّئ أي شيء (لا تُعد التسلسل).
يكون idempotent — نفس
delivery_idقد يصل أكثر من مرة.
فخ شائع في بعض الأطر: middleware يُعيد ترميز جسم JSON قبل أن يراه معالجك، فيُخفق sha256(body) بالمطابقة. الحلول:
Express: استعمل
express.raw({ type: 'application/json' })لمسار webhook ثمJSON.parse(req.body)في المعالج.Django:
request.bodyهي البايتات الخام — هذا ما تريده.Laravel:
$request->getContent()يُعيد الجسم الخام.PHP خام:
file_get_contents('php://input').
انظر التحقق من التواقيع لشيفرة كاملة بأربع لغات.