Docs

Webhooks

آخر تحديث: 2026-10-035 دقيقة قراءة

Webhook هو طلب POST يرسله GREM إلى عنوانك عندما يحدث شيء: تنتهي مهمة، أو يتغير عقار أو عميل محتمل. إدارة الاشتراكات تتطلب النطاق webhooks وهي مجانية. تُدار Webhooks الوكالة بمفتاح الوكالة كلها؛ ومفتاح الوكيل يتلقى 403.

الاشتراك

curl -X POST https://developers.grem.capital/api/v1/webhooks \
  -H "Authorization: Bearer gsk_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{"url":"https://example.com/grem-hook","events":["content.completed","content.failed","lead.created"]}'
{ "id": "...", "url": "https://example.com/grem-hook", "events": [...], "status": "active",
  "failureCount": 0, "lastDeliveryAt": null, "secret": "whsec_..." }
  • url: عنوان عام https:// على المنفذ الافتراضي (443)، دون user:password فيه. المنافذ الأخرى وبيانات الدخول في العنوان والعناوين الخاصة والمحلية والداخلية تُرفض (400). قاعدة المنفذ تُطبق عند تعيين العنوان: العناوين المحفوظة سابقا بمنفذ آخر تستمر في تلقي التسليمات.
  • events: حدث واحد على الأقل من القائمة أدناه؛ الأسماء غير المعروفة تُهمل.
  • secret: سر التوقيع. يُعاد مرة واحدة فقط، فاحفظه.
  • حتى 10 عناوين لكل حساب، ومنفصلةً حتى 10 عناوين لبيئة الاختبار أُنشئت بمفاتيح الاختبار؛ والعنوان الحادي عشر يُرفض بـ 422 business_rule.
  • العناوين المنشأة بمفتاح اختبار هي عناوين بيئة الاختبار: لا تتلقى إلا أحداث مهام بيئة الاختبار (بيئة الاختبار).

يمكن إدارة الاشتراكات من لوحة التحكم أيضا: API → Webhooks. ومن هناك يمكن تغيير سر التوقيع.

الإدارة

الطلبماذا يفعل
GET /webhooksاشتراكاتك (دون الأسرار)
PATCH /webhooks/{id}تغيير url أو events أو status (active / disabled)
DELETE /webhooks/{id}حذف الاشتراك
POST /webhooks/{id}/testإرسال حدث تجريبي؛ الجسم {"eventType": "content.completed"} يحدد الشكل
GET /webhooks/{id}/deliveriesآخر 50 عملية تسليم: الحالة، ورمز الرد، والزمن، وبداية الرد
POST /webhooks/{id}/deliveries/{deliveryId}/resendإعادة إرسال تسليم؛ deliveryId هو _id لسطر من السجل

PATCH دون أي حدث صالح في events أو بقيمة status غير active / disabled يُرفض بـ 400. وDELETE لاشتراك غير معروف يعيد 404. في سجل التسليمات payload هو data الخاص بالحدث.

الأحداث

المهام المنتهية

الحدثمتى
valuation.completed، valuation.failedتقرير التقييم الكامل
review.completed، review.failedمراجعة التقرير
content.completed، content.partial، content.failedحزمة المحتوى (partial: فشلت بعض الملفات)
text.completed، text.failedالنصوص
video.completed، video.failedالفيديو
avatar.completed، avatar.failedفيديو الأفاتار
photo.completed، photo.failedتحسين الصور
floorplan.completed، floorplan.failedالمخطط
pdf.completed، pdf.failedعرض PDF

data: { "jobId", "status", "state" }. status هي الحالة الخام للأداة بأحرف كبيرة (مثلا COMPLETED)؛ اعتمد على state في التفرع. إذا لم تنته المهمة خلال 30 دقيقة يصل .failed مع "reason": "timeout". لا تُتابَع المهمة إلا إذا كان الاشتراك في حدثها موجودا عند إنشائها (المهام والنتائج).

العقارات

الحدثمتى
object.createdأنشأت عقارا
object.updatedتغير عقار، بما في ذلك من قِبل مشرف
object.publishedأصبح العقار عاما
object.deletedحُذفت مسودة أو أُرشف عقار منشور

data: { "objectId", "kind", "status", "occurredAt" }؛ في object.deleted بدلا من status يأتي mode (deleted أو archived). البيانات الكاملة: GET /objects/{kind}/{id}.

العملاء المحتملون

الحدثمتى
lead.createdعميل محتمل جديد لك
lead.updatedتغيرت الحالة أو المسؤول

data: { "leadId", "status", "type", "source", "objectId", "occurredAt" }. لا توجد بيانات شخصية للعميل في الحدث: اقرأ العميل المحتمل عبر GET /leads/{id}.

Swipe Match

الحدثمتى
match.lead.offeredعُرض عليك (أو على وكيل في وكالتك) عميل محتمل
match.lead.offer_closedأُغلق العرض دونك (انتهت المهلة)
match.lead.matchedأُسند إليك العميل المحتمل
match.lead.updatedانتقل العميل المحتمل إلى مرحلة جديدة
match.lead.unassignedسُحب منك العميل المحتمل

data: { "leadId", "eventId", "eventCode", "occurredAt", "source", "stage", "active", "leadKind", "clientType", "country", "brokerId", "agencyId" }. اسم العميل وهاتفه وبريده لا ترد أبدا في الأحداث (Swipe Match).

صيغة التسليم

POST https://example.com/grem-hook
Content-Type: application/json
x-grem-event: content.completed
x-grem-signature: t=1759400000,v1=3b78a03b78058d229023db2a0b4ee072...

{ "id": "...", "type": "content.completed", "createdAt": "2026-10-02T10:00:00.000Z", "data": { ... } }
  • id ثابت في كل إعادات التسليم الواحد: استخدمه كي لا تعالج الحدث مرتين.
  • t وcreatedAt هما وقت المحاولة المعنية.
  • التسليم التجريبي (POST /webhooks/{id}/test) مع eventType من القائمة يحمل test: true في data؛ ودونه يكون نوع الحدث webhook.test. وeventType غير الموجود في القائمة يعطي 400. أما webhook.test نفسه فمقبول.

التحقق من التوقيع

التوقيع هو HMAC-SHA256 للسلسلة "<t>.<raw body>" بسر الاشتراك، بصيغة ست عشرية في v1. تحقق قبل أي معالجة، وارفض قيم t القديمة (حماية من إعادة الإرسال).

احسب التوقيع على الجسم الخام كما وصل، قبل تحليل JSON. إعادة التسلسل تغير البايتات فلا يتطابق التوقيع.

const crypto = require('crypto');

function verifyGremSignature(secret, header, rawBody, toleranceSec = 300) {
  const parts = Object.fromEntries(String(header || '').split(',').map((kv) => kv.split('=')));
  const t = Number(parts.t);
  if (!t || Math.abs(Date.now() / 1000 - t) > toleranceSec) return false;
  const expected = crypto.createHmac('sha256', secret).update(`${t}.${rawBody}`).digest('hex');
  const a = Buffer.from(expected);
  const b = Buffer.from(parts.v1 || '');
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

app.post('/grem-hook', express.raw({ type: 'application/json' }), (req, res) => {
  const raw = req.body.toString('utf8');
  if (!verifyGremSignature(process.env.GREM_WEBHOOK_SECRET, req.get('x-grem-signature'), raw)) {
    return res.status(400).send('invalid signature');
  }
  const event = JSON.parse(raw);
  // event.id, event.type, event.data
  res.sendStatus(200);
});

في SDK تقوم بذلك الدالة verifyWebhookSignature().

إعادة المحاولات والتعطيل

  • ينجح التسليم إذا أجاب خادمك بـ 2xx خلال 10 ثوان. أجب بسرعة ونفّذ العمل الثقيل في الخلفية.
  • يُعاد التسليم الفاشل بعد 5 دقائق و30 دقيقة وساعتين و6 ساعات: حتى 5 محاولات إجمالا.
  • بعد 5 عمليات تسليم فاشلة كليا على التوالي يُعطَّل الاشتراك (status: disabled). أعد تفعيله عبر PATCH /webhooks/{id} مع {"status":"active"} أو من لوحة التحكم.
  • سجل التسليم (GET /webhooks/{id}/deliveries) يعرض رمز رد خادمك وبداية جسم الرد: منه تعرف سبب فشل التسليم.

مقالات ذات صلة

هل كانت هذه المقالة مفيدة؟