تنتهي الأدوات الطويلة بشكل غير متزامن. وبدل الاستعلام المتكرر سجّل webhook، فترسل GREM حدثا بطلب POST إلى عنوانك عند اكتمال المهمة.
تسجيل نقطة نهاية
في اللوحة ضمن أدوات الذكاء الاصطناعي ← API ← Webhooks: أضف عنوان https:// عاما واختر الأحداث التي تريدها. ويعرض سر التوقيع (whsec_…) مرة واحدة - احفظه للتحقق من عمليات التسليم.
وتأتي الأحداث بالشكل <المصدر>.<النتيجة>. والمتاح منها:
الأدوات:
valuation.completed,valuation.failedcontent.completed,content.partial,content.failedtext.completed,text.failedphoto.completed,photo.failedavatar.completed,avatar.failedvideo.completed,video.failedfloorplan.completed,floorplan.failedreview.completed,review.failedpdf.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؛ واستخدم إرسال اختبار لإطلاق تسليم تجريبي، وراجع سجل عمليات التسليم.