Skip to Content
مرجع APIWebhooks

Webhooks (الاستقبال)

عندما تقع أحداث في مشروع SupDesk الخاص بك، تُسلّم الويب هوك الصادرة طلبات HTTP POST إلى نقاط النهاية التي ضبطتها. توثّق هذه الصفحة صيغة الحمولة وآلية التوقيع وسلوك إعادة المحاولة، حتى تتمكّن من بناء مستقبِلات ويب هوك موثوقة.

صيغة الحمولة

تستخدم كل عملية تسليم ويب هوك مغلّفًا قياسيًا:

{ "event": "post.created", "timestamp": "2026-07-15T12:00:00.000Z", "project_id": "550e8400-e29b-41d4-a716-446655440000", "data": { ... } }
الحقلالنوعالوصف
eventstringالحدث الذي أطلق التسليم
timestampstringطابع زمني ISO 8601 لوقت وقوع الحدث
project_idstringمعرّف UUID للمشروع الذي وقع فيه الحدث
dataobjectحمولة خاصة بالحدث (انظر أدناه)

الأحداث

post.created

أُرسل منشور ملاحظات جديد.

{ "event": "post.created", "data": { "id": "550e8400-e29b-41d4-a716-446655440000", "title": "Add dark mode", "type": "feature", "status": "open", "author_type": "end_user", "body": "It would be great to have a dark mode option.", "created_at": "2026-07-15T12:00:00Z" } }

post.updated

حُرِّر منشور ملاحظات.

{ "event": "post.updated", "data": { "id": "550e8400-e29b-41d4-a716-446655440000", "title": "Add dark mode (updated)", "type": "feature", "status": "open", "author_type": "end_user", "body": "Updated description.", "created_at": "2026-07-15T12:00:00Z" } }

post.deleted

حُذف منشور ملاحظات.

{ "event": "post.deleted", "data": { "id": "550e8400-e29b-41d4-a716-446655440000", "title": "Add dark mode", "type": "feature", "status": "open", "author_type": "end_user", "body": "It would be great to have a dark mode option.", "created_at": "2026-07-15T12:00:00Z" } }

post.status_changed

تغيّرت حالة منشور.

{ "event": "post.status_changed", "data": { "id": "550e8400-e29b-41d4-a716-446655440000", "title": "Add dark mode", "type": "feature", "old_status": "open", "new_status": "planned", "author_type": "member", "body": "We're planning this for Q3.", "created_at": "2026-07-15T12:00:00Z" } }

comment.created

أُضيف تعليق جديد إلى منشور.

{ "event": "comment.created", "data": { "id": "550e8400-e29b-41d4-a716-446655440000", "post_id": "550e8400-e29b-41d4-a716-446655440001", "post_title": "Add dark mode", "author_type": "end_user", "body": "Any update on this?", "created_at": "2026-07-15T12:00:00Z" } }

comment.updated

حُرِّر تعليق.

{ "event": "comment.updated", "data": { "id": "550e8400-e29b-41d4-a716-446655440000", "post_id": "550e8400-e29b-41d4-a716-446655440001", "post_title": "Add dark mode", "author_type": "end_user", "body": "Updated comment.", "created_at": "2026-07-15T12:00:00Z" } }

comment.deleted

حُذف تعليق.

{ "event": "comment.deleted", "data": { "id": "550e8400-e29b-41d4-a716-446655440000", "post_id": "550e8400-e29b-41d4-a716-446655440001", "post_title": "Add dark mode", "author_type": "end_user", "body": "Any update on this?", "created_at": "2026-07-15T12:00:00Z" } }

message.created

أُرسلت رسالة خاصة جديدة.

{ "event": "message.created", "data": { "id": "550e8400-e29b-41d4-a716-446655440000", "thread_id": "550e8400-e29b-41d4-a716-446655440001", "thread_subject": "Help needed", "sender": "end_user", "body": "I can't find the settings page.", "via": "web", "created_at": "2026-07-15T12:00:00Z" } }

message.updated

حُرِّرت رسالة خاصة.

{ "event": "message.updated", "data": { "id": "550e8400-e29b-41d4-a716-446655440000", "thread_id": "550e8400-e29b-41d4-a716-446655440001", "thread_subject": "Help needed", "sender": "end_user", "body": "Updated message.", "via": "web", "created_at": "2026-07-15T12:00:00Z" } }

message.deleted

حُذفت رسالة خاصة.

{ "event": "message.deleted", "data": { "id": "550e8400-e29b-41d4-a716-446655440000", "thread_id": "550e8400-e29b-41d4-a716-446655440001", "thread_subject": "Help needed", "sender": "end_user", "body": "I can't find the settings page.", "via": "web", "created_at": "2026-07-15T12:00:00Z" } }

beta_feedback.created

أرسل مختبِر بيتا ملاحظات على برنامج بيتا.

{ "event": "beta_feedback.created", "data": { "id": "550e8400-e29b-41d4-a716-446655440000", "program": "Acme 2.0 Beta", "category": "bug", "severity": "high", "title": "Crash on export", "body": "The app closes when I export a large file.", "created_at": "2026-07-15T12:00:00Z" } }

قد تكون severity بقيمة null.

beta_feedback.deleted

حُذف عنصر ملاحظات بيتا.

{ "event": "beta_feedback.deleted", "data": { "id": "550e8400-e29b-41d4-a716-446655440000", "program": "Acme 2.0 Beta", "category": "bug", "severity": "high", "title": "Crash on export", "body": "The app closes when I export a large file.", "created_at": "2026-07-15T12:00:00Z" } }

waitlist_signup.created

انضم زائر إلى قائمة الانتظار.

{ "event": "waitlist_signup.created", "data": { "id": "550e8400-e29b-41d4-a716-446655440000", "email": "jamie@example.com", "status": "waiting", "position": 42, "referral_count": 0, "referral_code": "a1b2c3", "source": "portal", "created_at": "2026-07-15T12:00:00Z", "invited_at": null, "joined_at": null } }

قيمة position هي null لمدخلات invited وjoined.

waitlist_signup.invited

دُعي مدخل في قائمة الانتظار من القائمة.

{ "event": "waitlist_signup.invited", "data": { "id": "550e8400-e29b-41d4-a716-446655440000", "email": "jamie@example.com", "status": "invited", "position": null, "referral_count": 0, "referral_code": "a1b2c3", "source": "portal", "created_at": "2026-07-15T12:00:00Z", "invited_at": "2026-07-15T12:00:00Z", "joined_at": null } }

waitlist_signup.joined

قبل مدخل مدعو وانضم.

{ "event": "waitlist_signup.joined", "data": { "id": "550e8400-e29b-41d4-a716-446655440000", "email": "jamie@example.com", "status": "joined", "position": null, "referral_count": 0, "referral_code": "a1b2c3", "source": "portal", "created_at": "2026-07-15T12:00:00Z", "invited_at": "2026-07-15T12:00:00Z", "joined_at": "2026-07-15T12:05:00Z" } }

csat_survey.completed

اكتمل استطلاع رضا العملاء بتقييم.

{ "event": "csat_survey.completed", "data": { "thread_id": "550e8400-e29b-41d4-a716-446655440000", "rating": 5, "comment": "Support was fast and friendly.", "created_at": "2026-07-15T12:00:00Z" } }

قيمة rating عدد صحيح بين 1 و5؛ وقد تكون comment بقيمة null.

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

تتضمّن كل عملية تسليم ترويسة X-SupDesk-Signature:

X-SupDesk-Signature: sha256=a1b2c3d4e5f6...

القيمة التي تلي sha256= هي HMAC-SHA256 بترميز ست عشري لجسم الطلب الخام، محسوبةً باستخدام سر التوقيع الخاص بالويب هوك لديك.

خطوات التحقق

  1. اقرأ جسم الطلب الخام (لا تحلّل JSON أولاً)
  2. احسب HMAC-SHA256(signing_secret, raw_body)
  3. قارن الملخّص الست عشري بالقيمة الموجودة في ترويسة التوقيع
  4. استخدم مقارنة بزمن ثابت لمنع هجمات التوقيت

مثال بلغة Node.js

import { createHmac, timingSafeEqual } from "crypto"; function verifySupDeskWebhook(secret, rawBody, signatureHeader) { const expected = createHmac("sha256", secret) .update(rawBody) .digest("hex"); const received = signatureHeader.replace("sha256=", ""); return timingSafeEqual(Buffer.from(expected), Buffer.from(received)); } // Express example app.post("/webhooks/supdesk", express.raw({ type: "application/json" }), (req, res) => { const sig = req.headers["x-supdesk-signature"]; if (!verifySupDeskWebhook(SECRET, req.body, sig)) { return res.status(401).json({ error: "Invalid signature" }); } const event = JSON.parse(req.body); // Process event... res.status(200).json({ ok: true }); });

مثال بلغة Python

import hmac import hashlib from flask import Flask, request, jsonify app = Flask(__name__) WEBHOOK_SECRET = "your-signing-secret" def verify_signature(secret: str, body: bytes, header: str) -> bool: expected = hmac.new(secret.encode(), body, hashlib.sha256).hexdigest() received = header.removeprefix("sha256=") return hmac.compare_digest(expected, received) @app.route("/webhooks/supdesk", methods=["POST"]) def handle_webhook(): sig = request.headers.get("X-SupDesk-Signature", "") if not verify_signature(WEBHOOK_SECRET, request.data, sig): return jsonify(error="Invalid signature"), 401 event = request.get_json() # Process event... return jsonify(ok=True), 200

مثال بلغة Ruby

require "openssl" require "sinatra" require "json" WEBHOOK_SECRET = "your-signing-secret" def verify_signature(secret, body, header) expected = OpenSSL::HMAC.hexdigest("SHA256", secret, body) received = header.sub("sha256=", "") Rack::Utils.secure_compare(expected, received) end post "/webhooks/supdesk" do body = request.body.read sig = request.env["HTTP_X_SUPDESK_SIGNATURE"] || "" unless verify_signature(WEBHOOK_SECRET, body, sig) halt 401, { error: "Invalid signature" }.to_json end event = JSON.parse(body) # Process event... status 200 { ok: true }.to_json end

إعادة المحاولة

عندما يفشل تسليم ويب هوك، تعيد SupDesk المحاولة بتراجع أُسّي:

المحاولةالتأخير
1فوري
2ثانية واحدة
3ثانيتان

الحالات التي يُعاد فيها المحاولة: استجابات 5xx، وأخطاء الشبكة، وانتهاء المهل (10 ثوانٍ).

لا يُعاد فيها المحاولة: استجابات 4xx (فأخطاء العميل تشير إلى مشكلة في التكوين).

الرد على الويب هوك

  • أعِد رمز حالة 2xx لإقرار الاستلام
  • استجب خلال 10 ثوانٍ لتجنّب إعادات المحاولة بسبب انتهاء المهلة
  • عالِج الحدث بشكل غير متزامن إن كان منطقك يستغرق وقتًا أطول
  • أعِد الاستجابة نفسها للتسليمات المكرّرة (التماثل)
Last updated on