Docs

Webhooks

آخر تحديث: 2026-07-213 دقيقة قراءة

تنتهي الأدوات الطويلة بشكل غير متزامن. وبدل الاستعلام المتكرر سجّل webhook، فترسل GREM حدثا بطلب POST إلى عنوانك عند اكتمال المهمة.

تسجيل نقطة نهاية

في اللوحة ضمن أدوات الذكاء الاصطناعي ← API ← Webhooks: أضف عنوان https:// عاما واختر الأحداث التي تريدها. ويعرض سر التوقيع (whsec_…) مرة واحدة - احفظه للتحقق من عمليات التسليم.

وتأتي الأحداث بالشكل <المصدر>.<النتيجة>. والمتاح منها:

الأدوات:

  • valuation.completed, valuation.failed
  • content.completed, content.partial, content.failed
  • text.completed, text.failed
  • photo.completed, photo.failed
  • avatar.completed, avatar.failed
  • video.completed, video.failed
  • floorplan.completed, floorplan.failed
  • review.completed, review.failed
  • pdf.completed, pdf.failed

الكائنات: object.created وobject.updated وobject.published وobject.deleted.

العملاء المحتملون: lead.created وlead.updated. وهي لا تحمل بيانات شخصية - بل إشارة إلى العميل الذي تغير فقط.

المحتوى

كل تسليم طلب POST بجسم JSON:

{
  "id": "…",
  "type": "content.completed",
  "createdAt": "2026-07-21T10:00:00.000Z",
  "data": { }
}

ومع هذه الترويسات:

x-grem-event: content.completed
x-grem-signature: t=1721556000,v1=3b78a03b78058d229023db2a0b4ee072…

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

تحقق من التوقيع دائما قبل الوثوق بأي تسليم. والتوقيع هو HMAC-SHA256 على السلسلة "<t>.<الجسم الخام>" بمفتاح هو سر webhook لديك. وt ختم زمني بصيغة Unix؛ ارفض الأختام القديمة لمنع إعادة التشغيل.

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

Node.js (Express)

const crypto = require('crypto');

function verifyGremSignature(secret, header, rawBody, toleranceSec = 300) {
  // header: "t=<unix>,v1=<hex>"
  const parts = Object.fromEntries(header.split(',').map((kv) => kv.split('=')));
  const t = Number(parts.t);
  if (!t || Math.abs(Date.now() / 1000 - t) > toleranceSec) return false; // stale
  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);
}

// Capture the RAW body for this route.
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);
  // handle event.type / event.data …
  res.sendStatus(200);
});

Python (Flask)

import hmac, hashlib, time
from flask import Flask, request

def verify_grem_signature(secret, header, raw_body, tolerance=300):
    parts = dict(kv.split('=', 1) for kv in header.split(','))
    t = int(parts.get('t', 0))
    if not t or abs(time.time() - t) > tolerance:
        return False  # stale
    expected = hmac.new(secret.encode(), f"{t}.{raw_body}".encode(), hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, parts.get('v1', ''))

app = Flask(__name__)

@app.post('/grem-hook')
def grem_hook():
    raw = request.get_data(as_text=True)  # RAW body, before parsing
    if not verify_grem_signature(GREM_WEBHOOK_SECRET, request.headers.get('x-grem-signature', ''), raw):
        return 'invalid signature', 400
    event = request.get_json()
    # handle event['type'] / event['data'] …
    return '', 200

التسليم وإعادة المحاولة والأمان

  • رد سريعا بحالة 2xx. ونفذ العمل الثقيل بشكل غير متزامن.
  • تعاد محاولة عمليات التسليم الفاشلة بتراجع متزايد؛ وبعد إخفاقات متكررة تعطل نقطة النهاية ويصلك إشعار.
  • ويجب أن تكون نقاط النهاية عناوين https:// عامة. أما العناوين الخاصة والمحلية والداخلية فترفض (حماية من SSRF).
  • ويمكن تبديل سر التوقيع في أي وقت من تبويب Webhooks؛ واستخدم إرسال اختبار لإطلاق تسليم تجريبي، وراجع سجل عمليات التسليم.

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