API Webhooks
مرجع كامل لإدارة اشتراكات Webhook برمجيًا. استخدم هذه النقاط لتسجيل نقاط نهاية HTTPS تستقبل إشعارات JSON عند وقوع أحداث في مستأجرك.
وارد مقابل صادر
تُعدّ هذه النقاط لضبط Webhooks الصادرة (وقتي → خادمك). إنها ليست عنوان URL الذي تستدعيه مزودو الدفع إلى وقتي.
قائمة اشتراكات Webhook
GET /v1/webhooksيُرجع جميع اشتراكات Webhook للمستأجر المصادق عليه. لا تُدرج الأسرار أبدًا في استجابات القائمة.
مثال الطلب
curl -X GET "https://api.waqti.sa/v1/webhooks" \
-H "Authorization: Bearer 3|a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0" \
-H "Accept: application/json"مثال الاستجابة
{
"data": [
{
"id": 7,
"name": "ERP sync",
"url": "https://integrations.example.com/waqti/webhook",
"events": [
"purchase_order.approved",
"invoice.paid",
"vendor.updated"
],
"is_active": true,
"failure_count": 0,
"last_triggered_at": "2026-03-28T09:12:33Z",
"created_at": "2026-01-10T14:20:00Z"
}
]
}إنشاء Webhook
POST /v1/webhooksيُنشئ اشتراكًا جديدًا. زوّد عنوان URL لاستقبال طلبات POST وقائمة أنواع الأحداث المشترك بها.
جسم الطلب
| الحقل | النوع | مطلوب | الوصف |
|---|---|---|---|
name | string | نعم | اسم العرض (حد أقصى 100 حرف) |
url | string | نعم | نقطة نهاية HTTPS تقبل POST من نوع application/json (حد أقصى 500 حرف) |
events | array | نعم | سلسلة واحدة أو أكثر من أنواع الأحداث (انظر الأحداث المتاحة) |
سر التوقيع المستخدم للتحقق من HMAC يُولَّد من وقتي ويُرجع فقط في هذه الاستجابة. لا يمكن تعيينه في جسم الطلب ولا يُعرض مجددًا في استجابات GET أو PUT لاحقًا — خزّنه بأمان عند إنشاء الاشتراك.
مثال الطلب
curl -X POST "https://api.waqti.sa/v1/webhooks" \
-H "Authorization: Bearer 3|a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{
"name": "Procurement automation",
"url": "https://integrations.example.com/waqti/webhook",
"events": [
"purchase_order.created",
"purchase_order.approved",
"invoice.created",
"vendor.created"
]
}'مثال الاستجابة
{
"data": {
"id": 8,
"name": "Procurement automation",
"url": "https://integrations.example.com/waqti/webhook",
"events": [
"purchase_order.created",
"purchase_order.approved",
"invoice.created",
"vendor.created"
],
"secret": "k8mN2pQ9rS4tU7vW1xY5zA6bC0dE3fG8hI2jK5lM9nO1pQ4rS7tU0vW3xY6z",
"is_active": true,
"created_at": "2026-04-02T11:05:22Z"
}
}تفاصيل Webhook
GET /v1/webhooks/{id}يُرجع اشتراكًا واحدًا بما في ذلك حقول الصحة. لا يُضمَّن سر التوقيع.
مثال الطلب
curl -X GET "https://api.waqti.sa/v1/webhooks/8" \
-H "Authorization: Bearer 3|a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0" \
-H "Accept: application/json"مثال الاستجابة
{
"data": {
"id": 8,
"name": "Procurement automation",
"url": "https://integrations.example.com/waqti/webhook",
"events": [
"purchase_order.created",
"purchase_order.approved",
"invoice.created",
"vendor.created"
],
"is_active": true,
"failure_count": 0,
"last_triggered_at": "2026-04-02T14:18:00Z",
"last_failed_at": null,
"last_failure_reason": null,
"created_at": "2026-04-02T11:05:22Z",
"updated_at": "2026-04-02T11:05:22Z"
}
}تحديث Webhook
PUT /v1/webhooks/{id}يُحدّث اشتراكًا قائمًا. جميع حقول الجسم اختيارية؛ الحقول المُهملة تحتفظ بقيمها الحالية.
جسم الطلب
| الحقل | النوع | الوصف |
|---|---|---|
name | string | اسم العرض (حد أقصى 100 حرف) |
url | string | عنوان التسليم (حد أقصى 500 حرف) |
events | array | قائمة بديلة لأنواع الأحداث (حد أدنى 1 إذا وُفِّر الحقل) |
is_active | boolean | تفعيل أو إيقاف التسليم مؤقتًا |
مثال الطلب
curl -X PUT "https://api.waqti.sa/v1/webhooks/8" \
-H "Authorization: Bearer 3|a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{
"events": [
"purchase_order.approved",
"purchase_order.rejected",
"purchase_order.cancelled",
"invoice.approved",
"invoice.paid",
"budget.threshold_reached"
],
"is_active": true
}'مثال الاستجابة
{
"data": {
"id": 8,
"name": "Procurement automation",
"url": "https://integrations.example.com/waqti/webhook",
"events": [
"purchase_order.approved",
"purchase_order.rejected",
"purchase_order.cancelled",
"invoice.approved",
"invoice.paid",
"budget.threshold_reached"
],
"is_active": true,
"updated_at": "2026-04-02T15:40:10Z"
}
}حذف Webhook
DELETE /v1/webhooks/{id}يُزيل الاشتراك نهائيًا. لا يُرسل تسليم بعد ذلك.
مثال الطلب
curl -X DELETE "https://api.waqti.sa/v1/webhooks/8" \
-H "Authorization: Bearer 3|a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0" \
-H "Accept: application/json"مثال الاستجابة
{
"data": null
}إرسال حدث اختبار
POST /v1/webhooks/{id}/testيضع في قائمة الانتظار تسليم اختبار فوريًا إلى عنوان URL المُعدّ بنفس الترويسات وقواعد التوقيع كالأحداث الحقيقية. يستخدم الحمولة نوع الحدث الاصطناعي webhook.test.
مثال الطلب
curl -X POST "https://api.waqti.sa/v1/webhooks/8/test" \
-H "Authorization: Bearer 3|a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0" \
-H "Accept: application/json"مثال الاستجابة (نجاح)
{
"data": {
"status_code": 200,
"response": "{\"received\":true}"
}
}إذا لم تُرجع نقطة النهاية حالة HTTP ناجحة، تُرجع API الاستجابة 422 مع رسالة خطأ تصف الفشل.
الأحداث المتاحة
اشترك باستخدام سلاسل حدث API في طلبات POST / PUT. عمود الاختصار يطابق التسميات الشائعة (po.*، إلخ.)؛ الخادم يقبل فقط القيمة الخاصة بـ API.
| حدث API | اختصار | الوصف |
|---|---|---|
purchase_order.created | po.created | أُنشئ أمر شراء جديد |
purchase_order.approved | po.approved | وُافق على أمر الشراء |
purchase_order.rejected | po.rejected | رُفض أمر الشراء |
purchase_order.cancelled | po.cancelled | أُلغي أمر الشراء |
invoice.created | invoice.created | أُنشئت فاتورة جديدة |
invoice.approved | invoice.approved | وُافقت على الفاتورة |
invoice.paid | invoice.paid | وُسِمَت الفاتورة كمدفوعة |
vendor.created | vendor.created | أُنشئ مورد جديد |
vendor.updated | vendor.updated | حُدّثت تفاصيل المورد |
budget.threshold_reached | budget.threshold_reached | تجاوزت الميزانية عتبة التنبيه |
قد تتوفر أنواع أحداث إضافية مع تطور المنتج؛ يجب أن تتجاهل تكاملك قيم event غير المعروفة التي لا تتعامل معها.
حمولة Webhook والتحقق من التوقيع
كل تسليم هو HTTP POST مع Content-Type: application/json. لهذا الشكل جسم JSON:
مثال جسم التسليم
{
"id": "9f2d5c1e-6b4a-4e8d-9c3f-1a7e2d5b8c60",
"event": "purchase_order.approved",
"created_at": "2026-04-02T16:22:11+00:00",
"tenant_id": "acme-corp",
"data": {
"purchase_order_id": 442,
"po_number": "PO-2026-0442",
"status": "approved",
"total_amount": 128750.5,
"currency": "SAR",
"approved_at": "2026-04-02T16:22:00+00:00"
}
}الترويسات
| الترويسة | الوصف |
|---|---|
X-Waqti-Event | نفس السلسلة كحقل event في الجسم |
X-Waqti-Signature | HMAC-SHA256 للجسم الخام للطلب بترميز hex باستخدام سر الاشتراك |
X-Waqti-Delivery | معرف التسليم الداخلي (مفيد للدعم وسجل idempotency) |
User-Agent | Waqti-Webhook/1.0 |
التحقق من HMAC SHA-256
- اقرأ جسم HTTP الخام كنص (قبل تحليل JSON).
- احسب
HMAC_SHA256(raw_body, secret)وترمّزه كسلسلة hexadecimal بحروف صغيرة. - قارن النتيجة مع
X-Waqti-Signatureبمقارنة زمن ثابت (مثلhash_equalsفي PHP).
إذا لم يطابق التوقيع، ارفض الطلب ولا تثق بـ JSON.
WARNING
المسافات البيضاء وترتيب المفاتيح وتطبيع Unicode يؤثر على الجسم الخام. تحقق من البايتات التي أرسلها وقتي فعليًا، وليس نسخة JSON أُعيد تسلسلها، ما لم يضمن إطار عملك مخرجات متطابقة.
سياسة إعادة المحاولة
تُعاد محاولة التسليمات الفاشلة (حالة HTTP غير 2xx، انتهاء المهلة، أو أخطاء شبكة) تلقائيًا. توقّع حتى نحو ثلاث إعادات بعد المحاولة الأولى، مع تراجع أسي بين كل محاولة. بعد استنفاد الحد الأقصى، يُعلَّم التسليم كفاشل.
إذا استمرت الإخفاقات، قد تزيد وقتي عدّاد الفشل على الاشتراك وتُعطّل Webhook تلقائيًا بعد فشل متتالي كثير — أصلح نقطة النهاية وأعد التفعيل عبر PUT أو واجهة الإدارة.
أفضل ممارسات أمنية
- استخدم HTTPS فقط لعناوين Webhook حتى تُشفَّر الحمولات والبيانات الوصفية أثناء النقل.
- تحقق من كل طلب بـ
X-Waqti-Signatureقبل التصرف؛ اعتبر التوقيع المفقود أو غير الصالح هجومًا أو خطأ إعدادًا. - خزّن السر من استجابة الإنشاء في مدير أسرار أو متغير بيئة، وليس في نظام التحكم بالإصدارات.
- استجب بسرعة بـ
2xxبعد التحقق ووضع العمل في طابور؛ نفّذ المعالجة الثقيلة بشكل غير متزامن حتى لا تنتهي مهلة وقتي وتُعاد المحاولة دون داعٍ. - صمّم لازدواجية التسليم: التسليم ممكن أن يكون at-least-once؛ استخدم
id(UUID في الحمولة) أو مفاتيح idempotency خاصة بك لإزالة التكرار.