→ المدونة
شروحات16 د قراءةThe Taskfolk team9 مشاهدةحُدِّث في

كيفية استخدام Webhooks

XLinkedIn

تتغيّر لوحتك ولا يعلم شيء خارج Taskfolk بذلك. يبقى Slack صامتًا. يتقادم مخزن التحليلات. ولا تسمع نوبة المناوبة قط أن WEB-142 انقلبت إلى In Review، فالنشر الذي كان ينتظرها يبقى جالسًا حتى ينظر أحد صدفةً.

هذه الفجوة هي ما تسدّه Webhooks في إدارة المشاريع. حين تتحرّك مهمة، أو يصل تعليق، أو ينضمّ عضو، يستطيع Taskfolk دفع طلب HTTP POST موقّع إلى عنوان تتحكّم فيه، لحظة حدوثه، فتتفاعل بقية منظومتك لحظيًا بدل الانتظار للاستطلاع التالي. يشرح هذا الدليل Webhooks داخل منتج Taskfolk من طرف إلى طرف: إنشاء واحد، والاشتراك في الأحداث الصحيحة، والتحقق من التوقيع بشكل صحيح (هناك جزء غريب فعلًا)، وكتابة مستهلك يبقى صحيحًا بالنظر إلى كيفية تسليم Taskfolk فعلًا. وفي النهاية نربط أحداث اللوحة مباشرةً بقناة Slack دون أي كود إطلاقًا.

ملاحظة نطاق واحدة قبل أن نبدأ. هذا تبويب المطوّر > Webhooks داخل مساحة عمل، جانب الدفع الصادر. إن أردت جانب القراءة (جلب المهام والتعليقات والمستندات عند الطلب)، فذلك يعيش في دليل واجهة REST. Webhooks هي الموارد نفسها، مدفوعةً إليك بدل سحبك لها.

لماذا تتفوّق Webhooks في إدارة المشاريع على استطلاع الـAPI

إليك شكل المشكلة التي يصطدم بها كل تكامل في النهاية. لوحتك هي سجل الحقيقة لما هو قيد العمل، وثلاث أو أربع أدوات أخرى تحتاج معرفة متى يتغيّر. الطريقة الواضحة هي الاستطلاع: اطرق واجهة REST على مؤقّت، وقارن النتيجة بما رأيته آخر مرة، وتصرّف بناءً على الفرق. يعمل، نوعًا ما. لكنك تعرف بالتغيير متأخّرًا حتى دقيقة، ومعظم استطلاعاتك لا تُرجع جديدًا، وتفوّت اللحظة الدقيقة التي حدث فيها الأمر، وهي غالبًا الجزء الوحيد الذي كان يهمّك.

تقلب Webhooks الاتجاه. بدل أن تسأل "هل من جديد؟" وفق جدول، يخبرك Taskfolk لحظة حدوث شيء. الموارد نفسها كواجهة REST، أشكال JSON نفسها، مسلَّمةً على الحدث بدل جلبها على ساعة.

لنجعله ملموسًا. WEB-142، "تبويب الجدول الزمني والملخّص"، جالسة في عمود In Review من مشروع WEB. يوافق مراجع عليها ويسحبها إلى Done. مع الاستطلاع، تعرف قناة Slack وخطّ النشر لديك كلّما جرى الاستطلاع التالي، قد يكون ثوانٍ، وقد يكون دقيقة تقريبًا.

مع Webhook، يحصل كلاهما على POST موقّع خلال ثانية تقريبًا من وصول السحب، والمهمة كاملة هناك في الجسم. يمكن للخطّ أن يبدأ؛ وتحصل القناة على سطرها النصّي. لا مؤقّت، ولا مقارنة، ولا تأخّر.

هذه الحجّة كلها. الآن لنبنِ واحدًا.

اعثر على تبويب Webhooks (ولماذا هو للمالك فقط)

كل شيء يعيش تحت وحدة تحكّم المطوّر. اذهب إلى /w/<workspace>/knowledge/developer وانتقل إلى تبويب Webhooks، أو اقفز إليه مباشرةً بـ?tab=webhooks. مفاتيح API والمهارات وWebhooks والاستخدام لمساحة العمل تجلس معًا في هذه المنطقة الواحدة.

وحدة تحكّم المطوّر داخل مساحة عمل، حيث تجلس تبويبات مفاتيح API والمهارات وWebhooks والاستخدام جنبًا إلى جنب.

قبل أن تتحمّس، فحص صلاحيات. لوحة المطوّر بأكملها محكومة بصلاحية workspace.edit_settings، وهي عمليًا المالكون والمشرفون. إن كنت عضوًا عاديًا أو مشاهدًا، فلن ترى أيًا من هذا: تعيد اللوحة توجيهك إلى صفحة مساحة العمل الرئيسية، ولتبويب Webhooks حالته الخاصة للمالك فقط تقرأ "‏Webhooks للمالك فقط" مع السطر "اطلب من مالك مساحة العمل أو مشرف إدارة Webhooks الصادرة." هذا ليس خللًا تلتفّ حوله. يحمل Webhook الصادر سرّ توقيع ويمكنه بثّ نشاط مساحة العمل إلى عنوان خارجي، فإنشاء واحد محصور عمدًا بمن يملكون إعدادات مساحة العمل.

بافتراض أن لك الدور، أول مرة تصل هنا تحصل على الحالة الفارغة: "لا Webhooks بعد" مع "أضف نقطة نهاية لاستقبال إشعارات أحداث موقّعة." وزر إضافة Webhook. ذلك الزر نقطة دخولك.

تبويب Webhooks تحت وحدة تحكّم المطوّر يعرض حالة "لا Webhooks بعد" الفارغة مع زر إضافة Webhook، محدّدًا أين تعيش الميزة ونقطة دخول إنشاء أول نقطة نهاية لك.

بمجرد أن يكون لديك Webhook واحد أو أكثر، تصبح هذه الواجهة نفسها جدولًا بدلًا من ذلك. أربعة أعمدة: نقطة النهاية (العنوان الذي تسلّم إليه)، والأحداث (الأنواع المشترك فيها)، والحالة (شارة مفعّل أو معطّل)، وآخر تسليم (رمز حالة HTTP لأحدث محاولة إضافةً إلى وقت نسبي، أو "أبدًا" إن لم ينطلق شيء بعد). يقسّم الجدول على صفحات عند 30 صفًا بزر تحميل المزيد، فلا تتحوّل مساحة عمل بكثير من التكاملات إلى تمرير لا نهائي.

أنشئ Webhook واختر أحداثك

انقر إضافة Webhook. النموذج قصير، ويتحقّق أثناء ملئك له.

نافذة إضافة Webhook: حقل عنوان نقطة النهاية بـhttps فقط، وتلميح التنسيق التلقائي لـSlack، وشبكة مربّعات اختيار أنواع الأحداث الـ14.

أولًا، عنوان نقطة النهاية. الحقل معنون "عنوان نقطة النهاية (https فقط)" بعنصر نائب مثل https://example.com/hooks/taskfolk. HTTPS مطلوب: يتحقّق النموذج من أن القيمة غير فارغة وتبدأ بـhttps://، وإن تركتها فارغة أو لصقت عنوان http:// فتحصل على "أدخل عنوان نقطة نهاية" أو "يجب أن تستخدم نقطة النهاية https://" على التوالي. لا مفرّ من قاعدة HTTPS، ولها سبب وجيه نصل إليه أدناه.

ثم الأحداث. تحصل على شبكة من 14 مربّع اختيار مسمّى، واحد لكل نوع حدث:

  • أحداث المهام: issue.created، issue.updated، issue.archived، issue.restored
  • أحداث التعليقات: comment.created، comment.updated، comment.deleted
  • أحداث المستندات: doc.created، doc.updated، doc.archived
  • أحداث الأعضاء: member.added، member.removed، member.role_changed
  • أحداث مساحة العمل: workspace.updated

أثناء تأشيرك للمربّعات يتحدّث عدّاد "{n} مختار"، وتحتاج واحدًا على الأقل وإلا رفض النموذج بـ"اختر حدثًا واحدًا على الأقل." حين تجهز، اضغط إنشاء Webhook (يعرض "جارٍ الإنشاء..." أثناء عمله).

إن فضّلت برمجته، فالأمر نفسه استدعاء واحد على واجهة REST، بمفتاح API له صلاحية webhooks:write:

curl -X POST "https://taskfolk.ai/api/v1/workspaces/taskfolk/webhooks" \
  -H "Authorization: Bearer tfk_live_a1b2..." \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com/hooks/taskfolk",
    "event_types": ["issue.created", "issue.updated"]
  }'
const res = await fetch("https://taskfolk.ai/api/v1/workspaces/taskfolk/webhooks", {
  method: "POST",
  headers: {
    Authorization: "Bearer tfk_live_a1b2...",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    url: "https://example.com/hooks/taskfolk",
    event_types: ["issue.created", "issue.updated"],
  }),
});
const webhook = await res.json(); // 201; includes "secret" exactly once
import requests

res = requests.post(
    "https://taskfolk.ai/api/v1/workspaces/taskfolk/webhooks",
    headers={"Authorization": "Bearer tfk_live_a1b2..."},
    json={
        "url": "https://example.com/hooks/taskfolk",
        "event_types": ["issue.created", "issue.updated"],
    },
)
webhook = res.json()  # 201; includes "secret" exactly once

يقبل مسار الـAPI شيئًا لا يقبله النموذج: "event_types": ["*"]، حرف البدل الذي يشترك في كل نوع حدث. استجابة الإنشاء أيضًا هي المكان الوحيد الذي يُرجع فيه الـAPI الـsecret بنصّه الصريح، وهذا يهمّ في القسم التالي.

نصيحتي: اشترك بضيق. تأشير كل شيء مغرٍ، لكن كل حدث لا تتصرّف بناءً عليه هو مجرد ضجيج يصل إلى نقطة نهايتك، مزيد للفلترة، مزيد للتسجيل، شكل آخر يتعثّر به مستهلكك. إن كل ما تريده حركة اللوحة تغذّي Slack، فـissue.updated (وربما issue.created) هو القصة كلها. أضف مزيدًا فقط حين يكون لديك سبب ملموس.

الآن الجزء الصادق. أحداث issue.* وcomment.* هي المشغّلات الحيّة الموصولة بموثوقية اليوم. تنطلق من مسارات الكتابة الحقيقية (إنشاء المهمة وتحديثها وأرشفتها واستعادتها وانتقال الحالة، وإنشاء التعليق وتحديثه وحذفه)، سواء جاء التغيير عبر واجهة REST أو إجراء مجمّع أو نقر أحدهم في الواجهة. أنواع doc.* وmember.* وworkspace.updated موجودة في الكتالوج ويمكنك الاشتراك فيها، لكن قبل أن تبني أي شيء يعتمد على انطلاق أحدها، أحدث تغييرًا حقيقيًا وتأكّد أن التسليم يصل فعلًا. عامل أحداث المهام والتعليقات كحاملة للثقل والبقية كـ"تحقّق أولًا".

قد تلمح أيضًا شارة "كل الأحداث" في مكان ما. تلك حرف بدل الـAPI أعلاه يظهر في الواجهة، لا مربّع اختيار على هذا النموذج. نافذة الإنشاء تقدّم الأنواع الـ14 المسمّاة فقط، فلا تبحث عن مربّع "كل الأحداث". ليس هنا.

انسخ سرّ التوقيع (تحصل عليه مرة واحدة فقط)

لحظة نقر إنشاء Webhook، يعرض Taskfolk خطوة كشف "أُنشئ Webhook"، وهذه الشاشة الوحيدة التي لا يمكنك تصفّحها بسرعة.

تعرض سرّ التوقيع: رمزًا مسبوقًا بـwhsec_، مع زر نسخ بجانبه. هذا السرّ هو كيف تثبت أن تسليمًا واردًا جاء فعلًا من Taskfolk ولم يزوّره من خمّن عنوان نقطة نهايتك. تحصل عليه مرة واحدة بالضبط. بعد هذه الشاشة، لا يعرض لك Taskfolk سوى "بادئة سرّ" من 8 أحرف للتعريف. السرّ الكامل مخزّن كتجزئة في جانبهم ولا يمكن عرضه ثانيةً أبدًا.

ولإيقافك عن تجاوزه بسرعة، هناك مربّع اختيار إلزامي: "نسخت سرّ التوقيع وخزّنته بأمان." يبقى زر تمّ معطّلًا حتى تؤشّره. ذلك الاحتكاك مقصود.

نافذة "أُنشئ Webhook" لمرة واحدة تعرض سرّ توقيع whsec وزر النسخ والتأكيد الإلزامي بأنك حفظته

فافعل الأمر الآمن الآن، قبل تمّ. انسخ السرّ وضعه حيث يقرؤه مستهلكك: مدير أسرار، أو على الأقل متغيّر بيئة على الخدمة التي تستقبل الـWebhook. لا تلصقه في محادثة أو تذكرة أو تعليق كود. إن فقدته فلا مسار استرداد. تدوّر السرّ وتحدّث مستهلكك بالجديد (مشروح لاحقًا). تخزينه بشكل صحيح هنا يوفّر عليك تلك الرحلة كلها.

كيف يبدو تسليم موقّع فعلًا

بمجرد أن يصبح الـWebhook حيًا، يصبح كل حدث مشترَك فيه طلب HTTP POST إلى نقطة نهايتك. معرفة الشكل الدقيق لذلك الطلب تجعل كتابة المستهلك سهلة.

sequenceDiagram
    participant B as Board
    participant U as Taskfolk
    participant C as Your endpoint
    B->>U: WEB-142 moves to Done
    U->>C: POST signed envelope
    C->>C: Verify signature
    C-->>U: 2xx within 15s
    Note over U,C: One attempt, no retry

على السلك، يبدو التسليم هكذا:

POST /hooks/taskfolk HTTP/1.1
Host: example.com
Content-Type: application/json
User-Agent: Taskfolk-Webhook/1.0
X-Taskfolk-Event: issue.updated
X-Taskfolk-Delivery: 0197f3a0-6c2e-7b31-a3d2-8f4e5b6c7d8e
X-Taskfolk-Signature: t=1752573120, v1=5f8a3c...

ماذا يعني كل ترويسة:

  • X-Taskfolk-Signature منسّقة كـt=<unix>, v1=<hex> (لاحظ المسافة بعد الفاصلة). t هو الطابع الزمني Unix حين وقّع Taskfolk الطلب؛ وv1 هو تجزئة HMAC بالست عشري التي تتحقّق مقابلها.
  • X-Taskfolk-Event هو نوع الحدث، مثل issue.updated.
  • X-Taskfolk-Delivery هو UUID يعرّف هذا التسليم بشكل فريد.
  • User-Agent هو Taskfolk-Webhook/1.0.

الجسم مغلّف JSON بشكل ثابت:

{
  "id": "<delivery-uuid>",
  "event": "issue.updated",
  "workspace": "<workspace-slug>",
  "created_at": "2026-07-15T10:32:00Z",
  "data": { "...the full resource..." }
}

تفصيلان يستحقان الحفر في الذاكرة. أولًا، id يساوي قيمة ترويسة X-Taskfolk-Delivery، ما يسلّمك مفتاح تكرارية طبيعيًا (احتفظ بهذه الفكرة، تهمّ أدناه). ثانيًا، وهذا الذي يوفّر عملًا حقيقيًا: data هو مورد REST الكامل للكيان المتأثّر، الكائن نفسه الذي كنت لتحصل عليه من REST GET على ذلك المورد. حين تتحرّك WEB-142 إلى Done، يكون كائن data هو المهمة الكاملة: المفتاح والعنوان والحالة والمُسند إليه، كله. لا تحتاج استدعاء API ثانيًا لجلب التفاصيل. حمَلها الـWebhook سلفًا. إن كنت تعرف شكل مهمة واجهة REST، فأنت تعرف هذه الحمولة سلفًا.

بضعة قيود تسليم تصمّم حولها: ينتظر Taskfolk حتى 15 ثانية لتستجيب نقطة نهايتك، ويعامل أي حالة 2xx نجاحًا، ويلتقط جسم استجابتك حتى حدّ 4 KB لسجل التسليم. فاستجب بسرعة، واستجب 2xx، وادفع المعالجة الثقيلة إلى ما بعد أن تكون قد أقررت، لا قبله.

تحقّق من التوقيع (مأزق sha256 للسرّ)

اقرأ هذا القسم مرتين. إصابته خطأً تعني فشل كل تسليم في فحصك بصمت، وستقسم أن الـWebhook معطوب بينما محقّقك هو المخطئ بخطوة واحدة.

الوصفة. للتحقق من تسليم:

  1. اقرأ ترويسة X-Taskfolk-Signature وقسّمها إلى t (الطابع الزمني) وv1 (التوقيع الست عشري).
  2. خذ جسم الطلب الخام، بالضبط كبايتات، قبل أي تحليل JSON وإعادة تسلسل. إعادة ترتيب المفاتيح أو إعادة تنسيق المسافات تغيّر التوقيع وتكسر الفحص.
  3. ابنِ السلسلة الموقّعة كـ<t>.<rawBody>: الطابع الزمني، ونقطة حرفية، ثم الجسم الخام.
  4. احسب HMAC-SHA256 على تلك السلسلة، مستخدمًا sha256(secret) مفتاحًا. ذلك تجزئة SHA-256 الست عشرية لسرّ توقيعك، لا السرّ الصريح.
  5. قارن مقارنة زمن ثابت تجزئتك الست عشرية المحسوبة مقابل قيمة v1 من الترويسة.

الخطوة 4 هي المأزق، وهي غير معتادة فعلًا. معظم أنظمة Webhook تُجري HMAC بالسرّ الخام. يُجري Taskfolk الـHMAC بتجزئة SHA-256 للسرّ. أدخل سلسلة whsec_... الصريحة مفتاحًا فلا تطابق تجزئتك أبدًا، وتحصل على صفر تسليمات صالحة دون خطأ يخبرك لماذا. جزّئ السرّ أولًا، واستخدم سلسلة التجزئة الست عشرية مفتاحًا.

في Node، المحقّق كله بضعة أسطر:

import { createHash, createHmac, timingSafeEqual } from "node:crypto";

function verifyTaskfolkSignature(rawBody, signatureHeader, secret) {
  const t = signatureHeader.match(/t=(\d+)/)?.[1];
  const v1 = signatureHeader.match(/v1=([0-9a-f]+)/)?.[1];
  if (!t || !v1) return false;

  const key = createHash("sha256").update(secret).digest("hex"); // the gotcha
  const expected = createHmac("sha256", key)
    .update(`${t}.${rawBody}`)
    .digest("hex");

  const a = Buffer.from(expected);
  const b = Buffer.from(v1);
  return a.length === b.length && timingSafeEqual(a, b);
}
import hashlib
import hmac

def verify_taskfolk_signature(raw_body: bytes, signature_header: str, secret: str) -> bool:
    parts = dict(p.strip().split("=", 1) for p in signature_header.split(","))
    t, v1 = parts.get("t"), parts.get("v1")
    if not t or not v1:
        return False

    key = hashlib.sha256(secret.encode()).hexdigest()  # the gotcha
    signed = f"{t}.".encode() + raw_body
    expected = hmac.new(key.encode(), signed, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, v1)

ملاحظتا أمان أخريان. ينتهي المقتطفان بمقارنة زمن ثابت، لا ==، فلا تسرّب معلومات توقيت؛ أبقِ ذلك في أي لغة تنقل إليها هذا. وفكّر في رفض التسليمات التي يكون t فيها أقدم من بضع دقائق. يجلس الطابع الزمني داخل السلسلة الموقّعة تحديدًا لتدافع عن نفسك ضد إعادة التشغيل. أصِب هذه الدالة مرة، اختبرها (القسم التالي)، ولا تفكّر فيها ثانيةً أبدًا.

اختبر نقطة نهايتك قبل أن تثق بها

لا تحتاج تحريك تذكرة حقيقية لفحص مستهلكك. يأتي Taskfolk بتسليم اختبار مدمج.

انقر صف Webhook في الجدول لفتح درج تفاصيله. في الداخل ترى عنوان نقطة النهاية بشارة مفعّل، وشارات الأحداث لما هو مشترَك فيه، وبادئة السرّ (تلك الأحرف الـ8، للتعريف)، وصف إجراءات: إرسال اختبار، وتدوير السرّ، وحذف.

إرسال اختبار يرسل حدث webhook.test مصطنعًا بـPOST مباشرةً إلى نقطة نهايتك ويبلّغ عن حالة HTTP التي أعادها خادمك في رسالة، مثل "أُرسل الاختبار (200)." تلك النقرة الواحدة تؤكّد السلسلة كلها: نقطة نهايتك قابلة للوصول، ومحقّق توقيعك يقبل طلبًا موقّعًا حقيقيًا من Taskfolk، وأنت تُعيد 2xx. إن أظهرت الرسالة غير 2xx أو خطأً، فقد التقطت المشكلة قبل أن يعتمد عليها أي حدث حقيقي.

عرض تفاصيل Webhook بعناصر تحكّم إرسال اختبار وتدوير السرّ وحذف فوق جدول التسليمات الأخيرة

المشغّل نفسه موجود على الـAPI، مفيد في نص نشر:

curl -X POST "https://taskfolk.ai/api/v1/workspaces/taskfolk/webhooks/<webhook-id>/test" \
  -H "Authorization: Bearer tfk_live_a1b2..."

قيد واحد: لا يمكنك اختبار Webhook معطّل. إن كان مطفأً، فالزر معطّل وترى "فعّل الـWebhook قبل إرسال اختبار." فعّله أولًا، ثم اختبر.

اجعل هذا عادة. كل مرة تمسّ فيها المحقّق أو تعيد نشر المستهلك، اضغط إرسال اختبار وراقب الـ200. إنها أرخص ثقة ستشتريها طوال اليوم.

الحدود الصادقة: لا إعادة محاولة، ولا إعادة تشغيل، صالِح عبر REST

هذا أهمّ قسم في الدليل، لأنه حيث يفقد مستهلك ساذج البيانات بصمت.

تسليم Taskfolk عمدًا "مرة واحدة على الأكثر" وببذل أفضل جهد. تقول ملاحظة المنتج نفسها في الدرج ذلك صراحةً: "التسليمات مرة واحدة على الأكثر وببذل أفضل جهد: لا إعادة محاولة تلقائية. صالِح الحالة عبر واجهة REST، واستخدم إرسال اختبار للتحقق من نقطة نهايتك." اقرأها حرفيًا. إن فشل POST التسليم، لأن نقطة نهايتك متوقّفة، أو انتهت مهلتها بعد الـ15 ثانية، أو أعادت 500، فيسجّل Taskfolk الفشل ويُسقطه. لا يُعاد المحاولة أبدًا. لا تراجع أسّي، ولا طابور رسائل ميتة تصرّفه لاحقًا.

flowchart LR
    A[Event fires] --> B[One POST attempt]
    B -->|2xx| C[Logged as success]
    B -->|error or timeout| D[Logged and dropped]
    D --> E[Reconcile via REST]

ما يعني أن تسليمًا قد يُفقد، وعليك أن تبني لذلك. قاعدتان تتبعان.

أولًا، اجعل مستهلكك تكراريًا، مفتاحه id التسليم (الـUUID في X-Taskfolk-Delivery، وأيضًا id في المغلّف). لأنك ستعيد المعالجة أحيانًا، أزل التكرار على ذلك المعرّف فلا يضرّ التصرّف مرتين.

ثانيًا، وهذا الأكبر، عامل Webhooks كطبقة إشعار سريعة، لا مصدر الحقيقة. صالِح مقابل واجهة REST دوريًا، لأن واجهة REST مرجعية. إن كان على قناة Slack أو مخزن التحليلات ألا يفوّتا تغيّر حالة أبدًا، فلا تفترض وصول كل حدث: استطلع واجهة REST على وتيرة بطيئة (كل بضع دقائق، لا كل بضع ثوانٍ) لتلتقط أي شيء أسقطته طبقة الدفع. Webhooks تجعلك سريعًا. مصالحة REST تجعلك صحيحًا. تريد كليهما. إنه بالضبط النمط الذي كنت لتبنيه لو كنت تدع وكيل ذكاء اصطناعي يدير لوحتك عبر واجهة REST: تفاعل مع الدفع، وتحقّق مقابل السحب.

يعرض الدرج أيضًا جدول "التسليمات الأخيرة"، ويساعد أن تعرف ما هو وما ليس. هو سجل للقراءة فقط، موسوم "سجل فقط، لا إعادة تشغيل"، مسحوب من ClickHouse: حتى 50 محاولة أحدث. كل صف يعرض:

العمود المعنى
الحدث نوع الحدث المسلَّم، مثل issue.updated
الحالة رمز استجابة HTTP، أو ERR إن فشل الطلب نفسه
المحاولة دائمًا 1؛ هناك محاولة واحدة فقط لكل تسليم
الكمون زمن الذهاب والإياب بالملي ثانية
متى طابع زمني نسبي للمحاولة

مفيد فعلًا لتصحيح الأخطاء ("هل أعادت نقطة نهايتي 500 عند 10:32؟"). لكن لاحظ ما ينقص. أجسام الطلبات غير مخزّنة، فلا زر إعادة إرسال أو إعادة تشغيل في أي مكان. ولا تقرأ عمود المحاولة كعدّاد إعادة محاولة. ليس كذلك. قبل انطلاق أي أحداث ترى "لا تسليمات بعد" مع "تظهر التسليمات هنا بمجرد انطلاق حدث."

فالنموذج الذهني بسيط: يحاول Taskfolk مرة، يخبرك بما حدث، ويمضي. مهمّة مستهلكك أن يكون موثوقًا في جانبه ويصالح البقية من REST.

دوّر الأسرار، وقواعد HTTPS/SSRF

شيئان تشغيليان ستحتاجهما في النهاية: تدوير السرّ، وفهم لماذا "اختبر مقابل localhost فحسب" لن يعمل.

تدوير السرّ. في الدرج، تدوير السرّ يصكّ سرّ whsec_ جديدًا ويعرضه مرة واحدة، مضمّنًا، تحت "سرّ توقيع جديد، يُعرض مرة واحدة:" (مع تأكيد "دُوّر السرّ"). انسخه فورًا، الانضباط نفسه كالإنشاء: مرة، ثم البادئة فقط.

سلوك التداخل يسهل وصفه خطأً، فإليكه بدقّة. بعد التدوير، تبقى تجزئة السرّ السابق صالحة للتحقق لنافذة 24 ساعة، لكن تلك النافذة توجد في جانب المستهلك فقط، لتمنح محقّقك وقتًا للّحاق. يوقّع Taskfolk دائمًا التسليمات الجديدة بالسرّ الحالي (الجديد). ليست فترة تبقى فيها تسليمات موقّعة بالسرّ القديم تصل. عمليًا: دوّر، وانشر مستهلكك بالسرّ الجديد، وأثناء الانتقال اقبل توقيعًا يطابق المفتاح القديم أو الجديد. بمجرد أن تنتقل، أسقط القديم.

قواعد الشبكة. يجب أن تكون نقاط النهاية HTTPS عامة. يرفض حارس SSRF، وقت الإنشاء ووقت التسليم معًا:

  • localhost و*.localhost
  • أسماء المضيفين أحادية التسمية
  • العناوين التي فيها user:pass@ معلومات مستخدم
  • نطاقات IP الخاصة أو المحجوزة

كما لن يتبع التسليم إعادات توجيه 3xx. هذا دفاع قياسي ضد خداع خادم ليستدعي عناوين داخلية، وهو غير قابل للتهيئة.

النتيجة العملية: لا يمكنك توجيه Webhook إلى http://localhost:3000 لاختباره محليًا. للتطوير مقابل جهازك، شغّل نفق HTTPS عام (ngrok، أو Cloudflare Tunnel، أو ما تحبّ) وأعطِ Taskfolk عنوان HTTPS للنفق. عندها يصل إرسال اختبار إلى خادمك المحلي عبر النفق تمامًا كما كان تسليم حقيقي ليصل.

الحذف. الحذف خلف نافذة تأكيد: "حذف هذا الـWebhook؟" مع "ستتوقّف التسليمات إلى هذه النقطة فورًا. لا يمكن التراجع عن هذا." الحذف فوري ومحصور في مساحة عملك. حين تتقاعد تكامل، احذف Webhook الخاص به فلا توقّع أحداثًا وتشحنها إلى فراغ.

أرسل أحداث Taskfolk مباشرةً إلى Slack

إليك الثمرة التي لا تحتاج كودًا تقريبًا.

أنشئ Slack Incoming Webhook في مساحة عمل Slack لديك (يسلّمك Slack عنوانًا على hooks.slack.com). ثم أنشئ Webhook في Taskfolk يشير إلى عنوان Slack ذاك، واشترك فيه في الأحداث التي تريدها في القناة (issue.updated هو الواضح لحركة اللوحة)، وانتهيت. يكشف Taskfolk هدف hooks.slack.com وينسّق كل تسليم تلقائيًا إلى رسالة Slack { text, blocks } (الـtext هو السطر الاحتياطي العادي، وblocks يحمل السطر نفسه كقسم mrkdwn). لا طبقة تحويل، ولا خدمة وصل، ولا تحليل في جانبك.

فحين تنقلب WEB-142 إلى Done، ما يصل إلى Slack ليس المغلّف الخام بل رسالة جاهزة:

{
  "text": "Updated issue *WEB-142*: Timeline + Summary tab (done) - taskfolk",
  "blocks": [
    { "type": "section", "text": { "type": "mrkdwn", "text": "Updated issue *WEB-142*: Timeline + Summary tab (done) - taskfolk" } }
  ]
}

يظهر سطر في قناة فريقك من تلقاء نفسه. رأيت التلميح لهذا سابقًا في نموذج الإنشاء: "تلميح: الصق عنوان Slack Incoming Webhook وتُنسَّق التسليمات تلقائيًا لـSlack." ذلك التلميح هو التكامل كله.

نتيجة واحدة لتستوعبها: لأن Slack ليس لديه ما يتحقّق مقابله، لا يرسل Taskfolk ترويسة X-Taskfolk-Signature إلى أهداف Slack. الجسم رسالة Slack، لا المغلّف الموقّع الخام. فلا تكتب كود تحقّق توقيع لنقطة نهاية Slack. لا توقيع لفحصه، وما كان Slack ليعرف ما يفعل بواحد. تحقّق التوقيع لمستهلكيك أنت المستقبِلين للمغلّف الخام. Slack هو الحالة الخاصة التي تتخطّاه.

هذا أسرع Webhook تُقيمه والذي تلجأ إليه معظم الفرق أولًا. وجّهه إلى قناة، وأرسل اختبارًا لتأكيد وصول الرسالة، وتصبح لوحتك تحدّث Slack.

إن كنت تربط وكلاء لا قنوات بهذا التدفق، فالمغلّف الموقّع نفسه هو ما يقرؤه مستهلك آلي، ويتناسب طبيعيًا مع أنماط القراءة والكتابة في كيفية ربط وكيل ذكاء اصطناعي ومقارنة MCP مقابل REST حين تقرّر كيف يردّ ذلك الوكيل إلى Taskfolk.

أقِم Webhook واحدًا اليوم، وجّهه إلى الأداة التي هي حاليًا آخر من يعلم، واضغط إرسال اختبار لتراه يصل.

أسئلة شائعة

أين أجد Webhooks في Taskfolk، ومن يمكنه إنشاؤها؟

اذهب إلى /w//knowledge/developer وانتقل إلى تبويب Webhooks (?tab=webhooks). منطقة المطوّر بأكملها للمالك والمشرف فقط، محكومة بصلاحية workspace.edit_settings. يُعاد توجيه الأعضاء والمشاهدين ويرون رسالة "Webhooks للمالك فقط"، فتحتاج مالك مساحة عمل أو مشرفًا لإدارتها.

كيف أتحقّق من توقيع Webhook في Taskfolk، ولماذا يستخدم تجزئة SHA-256 للسرّ بدل السرّ نفسه؟

أعد حساب HMAC-SHA256 على السلسلة .، حيث t هو الطابع الزمني من ترويسة X-Taskfolk-Signature وrawBody هو جسم الطلب غير المحلَّل، ثم قارن مقارنة زمن ثابت تجزئتك الست عشرية مقابل قيمة v1 في تلك الترويسة. مفتاح الـHMAC هو sha256(secret)، تجزئة SHA-256 لسرّ توقيعك، لا النصّ الصريح. هذا التفصيل الذي يفوت الناس: أدخل سلسلة whsec_ الخام مفتاحًا فلن يطابق أبدًا. جزّئ السرّ أولًا.

هل يعيد Taskfolk محاولة تسليمات Webhook الفاشلة؟

لا. التسليم مرة واحدة على الأكثر وببذل أفضل جهد. يُسجَّل POST الفاشل ويُسقط، ولا يُعاد المحاولة قط، بلا تراجع وبلا طابور. ابنِ مستهلكًا تكراريًا وصالِح الحالة المرجعية مقابل واجهة REST فلا يكلّفك تسليم مفقود بيانات.

هل يمكنني إعادة تشغيل تسليم Webhook سابق أو إعادة إرساله؟

لا. جدول التسليمات الأخيرة سجل للقراءة فقط (الحدث، والحالة، والمحاولة، والكمون، ومتى) حتى 50 محاولة أحدث، وأجسام الطلبات غير مخزّنة، فلا زر إعادة تشغيل أو إعادة إرسال. الطريقة الوحيدة لإطلاق تسليم عند الطلب هي إرسال اختبار، الذي يرسل حدث webhook.test مصطنعًا بـPOST.

أي أنواع أحداث يمكن لـWebhook الاشتراك فيها في Taskfolk؟

يسرد نموذج الإنشاء 14 مربّع اختيار مسمّى: issue.created، وissue.updated، وissue.archived، وissue.restored، وcomment.created، وcomment.updated، وcomment.deleted، وdoc.created، وdoc.updated، وdoc.archived، وmember.added، وmember.removed، وmember.role_changed، وworkspace.updated. أحداث issue.* وcomment.* هي المشغّلات الحيّة الموصولة بموثوقية؛ تأكّد من انطلاق أنواع المستندات والأعضاء ومساحة العمل في إعدادك قبل الاعتماد عليها. حرف بدل "كل الأحداث" مفهوم في الـAPI ونموذج البيانات يُعرض كشارة، لا مربّع اختيار على النموذج.

هل أحتاج استدعاء API ثانيًا للحصول على البيانات الكاملة بعد استقبال Webhook؟

لا. حقل data في المغلّف هو مورد REST الكامل للكيان المتأثّر، مطابق لما كان REST GET ليُرجعه. حين تتحرّك مهمة، تكون المهمة الكاملة في الحمولة سلفًا، فلا حاجة لجلب استرجاعي.

كيف أرسل أحداث لوحة Taskfolk إلى قناة Slack؟

أنشئ Slack Incoming Webhook (عنوان hooks.slack.com)، ثم أنشئ Webhook في Taskfolk يشير إليه واشترك في الأحداث التي تريدها. ينسّق Taskfolk التسليمات إلى أهداف Slack تلقائيًا إلى رسالة { text, blocks } دون أي إعداد إضافي. لا تستقبل أهداف Slack ترويسة X-Taskfolk-Signature، فلا تكتب تحقّق توقيع لها.

لماذا لا أستطيع اختبار Webhook مقابل http://localhost؟

لسببين. يجب أن تكون نقاط النهاية HTTPS (يرفض النموذج أي شيء ليس https://)، وحارس SSRF يرفض localhost والمضيفين أحاديي التسمية ومعلومات المستخدم في العنوان وعناوين IP الخاصة أو المحجوزة وقت الإنشاء ووقت التسليم معًا، دون اتّباع إعادات توجيه 3xx. للتطوير محليًا، شغّل نفق HTTPS عامًا وأعطِ Taskfolk عنوان النفق.

قراءات ذات صلة

أضف تعليقًا

ابدأ النقاش.