استقبال Webhooks من Narwhal خطوة بخطوة

أنشئ وجهة webhook باستخدام عنوان HTTPS الخاص بك والبلدان والعائلات التي تريدها، ثم تحقق من توقيع كل عملية تسليم قبل الوثوق بها. يمكن لكل حساب الاحتفاظ بما يصل إلى 25 وجهة، وتكلّف كل عملية تسليم استدعاءً واحدًا.

إنشاء وجهة واستقبال التسليمات

تستخدم Narwhal حسابًا واحدًا ومفتاح API واحدًا عبر واجهات بياناتها، ويشترك REST وMCP في المفتاح نفسه. احتفظ به في متغيّر البيئة NARWHAL_API_KEY وأرسله بالصيغة Authorization: Bearer YOUR_KEY إلى https://api.narwhalapi.com.

  1. إنشاء الوجهة أرسل عنوان HTTPS ورموز الدول ومعرّفات (slug) العائلات إلى POST /v1/webhooks/destinations. يمكن أن يصل طول العنوان إلى 2,048 حرفًا؛ اختر من 1 إلى 60 رمز دولة بصيغة alpha-3 أو WLD، ومن 1 إلى 14 معرّف عائلة، منها holidays وpredictions وfilings. ويمكن أن يتضمن الطلب أيضًا enabled: true. يختار طلب curl الدولتين USA وMEX مع العائلتين prices وlabour. استبدل عنوان HTTPS في المثال بالعنوان الذي يستقبل فيه المستقبِل التسليمات.
    إنشاء وجهة Webhook
    curl -s -X POST https://api.narwhalapi.com/v1/webhooks/destinations \
      -H "Authorization: Bearer $NARWHAL_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{"url":"https://example.com/narwhal",
           "countries":["USA","MEX"],"families":["prices","labour"]}'
  2. حفظ سرّ الوجهة تتضمن الاستجابة destination_id وsecret، ويُعرض secret مرة واحدة فقط. اضبط NARWHAL_WEBHOOK_SECRET على هذا السرّ للمستقبِل. احتفظ بمعرّف الوجهة للطلبات التي تخاطب هذه الوجهة، ومنها طلب التسليم التجريبي. استخدم السرّ المعاد لحساب توقيعات التسليم؛ أما مفتاح API فيبقى في ترويسة authorization لطلبات API. والسرّ المعاد من rotate-secret يُعرض مرة واحدة أيضًا؛ وخلال 24 ساعة بعد التدوير تحمل ترويسة التوقيع قيمة ثانية موقّعة بالسرّ القديم.
  3. التحقق من التوقيعات قبل التصرف يقارن المستقبِل أدناه التوقيعات في زمن ثابت ويرفض الطوابع الزمنية الأقدم من خمس دقائق. اقبل التسليم إذا طابقت أي قيمة توقيع. ولدى Python SDK أيضًا دالة تحقق عبر from narwhalapi.webhooks import verify. أعِد 2xx سريعًا ونفّذ العمل بعد ذلك.
    Python (Flask)، مستقبِل يتحقق من التوقيع
    import hashlib, hmac, json, os, time
    from flask import Flask, abort, request
    
    app = Flask(__name__)
    SECRET = os.environ[
        "NARWHAL_WEBHOOK_SECRET"
    ]  # returned once when you create the destination
    seen = set()  # use a database table in production
    
    
    @app.post("/narwhal")
    def narwhal():
        raw = request.get_data()  # exact bytes, before any JSON parsing
        stamp = request.headers.get("x-narwhal-timestamp", "")
        if not stamp.isdigit() or abs(time.time() - int(stamp)) > 300:
            abort(400)  # missing, or older than five minutes
        expected = (
            hmac.new(SECRET.encode(), f"{stamp}.".encode() + raw, hashlib.sha256)
            .hexdigest()
            .encode()
        )
        header = request.headers.get("x-narwhal-signature", "")
        sent = [part.strip().removeprefix("sha256=").encode() for part in header.split(",")]
        if not any(hmac.compare_digest(expected, candidate) for candidate in sent):
            abort(401)  # a missing or malformed header lands here, not in a 500
        try:
            event = json.loads(raw)
        except ValueError:
            abort(400)
        if not isinstance(event, dict):
            abort(400)
        if event.get("event_id") in seen:  # retries and replays repeat the same event_id
            return "", 200
        seen.add(event.get("event_id"))
        print(event.get("type"), event.get("country"), event.get("family"))
        return "", 200
  4. طلب تسليم تجريبي استدعِ POST /v1/webhooks/destinations/{destination_id}/test مع معرّف الوجهة المحفوظ. يحمل التسليم التجريبي is_test: true ولا يحتوي على مؤشر. حالات التسليم هي pending وleased وsucceeded وfailed وcancelled. يُرجع إنشاء وجهة تتجاوز الحد 409 limit_reached.

تشمل أنواع الأحداث release.scheduled وrelease.rescheduled وrelease.cancelled وrelease.updated؛ وdataset.published وdataset.revised؛ وyield_curve.published وyield_curve.revised؛ وholiday.calendar.revised. وللتنبؤات prediction.event.closed وprediction.event.settled. وللإفصاحات filing.published. استخدم الحقول type وcountry وfamily في الحمولة عند معالجة الحدث.

يعيد المستقبِل 400 عند غياب الطابع الزمني أو عدم صلاحيته، وعندما يتعذر تحليل المحتوى كائن JSON. ويعيد 401 عند غياب التوقيع أو سوء تنسيقه. وبعد نجاح مقارنة التوقيع يحلّل المستقبِل المحتوى قبل قراءة حقول الحدث وتسجيل معرّفه.

الحصول على الإفصاحات الجديدة للشركات التي تتابعها

أضف عائلة filings وقائمة companies إلى الوجهة. ترسل Narwhal بعد ذلك حدث filing.published في كل مرة تقدّم فيها إحدى هذه الشركات إيداعًا. وبالنسبة إلى إيداع حصة 13D أو 13G، ترسل حدثًا أيضًا عندما تكون الشركة المدرجة هي الشركة التي تقع فيها الحصة. يمكن لكل حساب متابعة ما يصل إلى 1,000 شركة.

متابعة إفصاحات Apple
curl -s -X POST https://api.narwhalapi.com/v1/webhooks/destinations \
  -H "Authorization: Bearer $NARWHAL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url":"https://example.com/narwhal","countries":["USA"],
       "families":["filings"],"companies":["usa:0000320193"]}'

تُحدَّد الشركات عبر company_id في Filings API، مثل usa:0000320193 لشركة Apple. قائمة companies مطلوبة مع عائلة filings ويُرفض الطلب من دونها. وتعيد الشركة غير المعروفة الخطأ 400 مع ذكر المعرّفات التي لم يُعثر عليها. ما زال الطلب يحتاج إلى countries، لكن الدول لا تضيّق الإفصاحات.

حمولة حدث الإفصاح
{
  "schema_version": "1",
  "event_id": "...",
  "cursor": "1",
  "type": "filing.published",
  "country": "USA",
  "recorded_at": "2026-10-30T21:05:12Z",
  "filing": {
    "filing_id": "0000320193-26-000106",
    "company_id": "usa:0000320193",
    "form": "10-K",
    "type": "annual",
    "fy": 2026,
    "fp": "FY",
    "period_end": "2026-09-26",
    "filed_at": "2026-10-30T21:01:40Z"
  }
}

استخدم filing.filing_id لجلب الإفصاح من Filings API. تُحذف الحقول fy وfp وperiod_end عندما لا يكون للإفصاح قيم لها، كما في معظم إفصاحات الأحداث والملكية.

استقبال Webhooks بلا برمجة

يمكن لـ Zapier Catch Hook أو Make Custom webhook أو عقدة n8n Webhook أن توفّر عنوان الوجهة. ضع هذا العنوان في الحقل url للوجهة واختر الدول والعائلات للتدفق. أبقِ عنوان catch hook سرّيًا: فهذه الوجهات بلا برمجة لا تتحقق لك من توقيع Narwhal.

يتطلب التحقق من التوقيع شيفرة مثل مستقبِل Python أعلاه. وإذا احتجت إلى هذا التحقق فاستخدم المستقبِل للتسليمات الواردة. يضم مرجع Webhooks عمليات الوجهات وسجل التسليم وإعادة التشغيل، ويصف مرجع الأحداث سجل REST لقراءة الأحداث بدءًا من مؤشر.

اختيار مستقبِل أو الاستعلام الدوري

الحالةالخيار الأنسبالسبب
خدمة خاصة بكمستقبِل Pythonيتحقق من التوقيع ويعالج معرّفات الأحداث المكررة
تدفق بلا برمجةcatch hook في Zapier أو Make أو n8nيوفّر عنوان الوجهة؛ أبقِ هذا العنوان سرّيًا
الاستعلام الدوري بدلًا من التسليماتGET /v1/events أو التقويم الاقتصادييعمل السجل بمؤشر متابعة؛ ويغطي التقويم التواريخ

لماذا يُتحقَّق من محتوى Webhook الخام؟

يغطي التوقيع البايتات الدقيقة لمحتوى الطلب، لذا فإن تحليله ثم إعادة تسلسله يغيّر ما تقارنه. اقرأ x-narwhal-timestamp على أنه ثوانٍ بنظام Unix، ثم اربط قيمته بنقطة ثم بالمحتوى الخام. واحسب HMAC-SHA256 بصيغة سداسية عشرية بأحرف صغيرة باستخدام السرّ المرمّز بـ UTF-8.

تحمل ترويسة x-narwhal-signature القيمة sha256=<hex>. قارن القيمة المحسوبة بالتوقيع المرسل في زمن ثابت قبل تحليل JSON، كما يفعل المستقبِل.

ماذا لو توقف خادم Webhook لديّ؟

تُعاد محاولات التسليم، ويتيح لك سجل التسليم رؤية الإخفاقات وإعادة تشغيل تسليم. استخدم GET .../deliveries لسجل الوجهة، ثم POST .../deliveries/{delivery_id}/replay للتسليم الذي تريد استقباله مرة أخرى. وتكرر إعادة التشغيل الحدث نفسه.

للّحاق بالأحداث من موضع محفوظ، اقرأ GET /v1/events بدءًا من آخر مؤشر لديك. احتفظ بهذا المؤشر لمسار السجل؛ فعملية التسليم التجريبي لا توفّر مؤشرًا.

هل يمكن أن أستقبل الحدث نفسه مرتين؟

نعم، فإعادة المحاولة وإعادة التشغيل تكرران event_id نفسه. استخدمه مفتاحًا لضمان عدم التكرار (idempotency) وخزّنه قبل التصرف في الحدث. وفي الإنتاج استخدم جدول قاعدة بيانات للمعرّفات المخزّنة، كما يشير تعليق المستقبِل.

يحتفظ المستقبِل في المثال بالمعرّفات في seen. وعندما يكون المعرّف موجودًا يعيد 200 دون طباعة الحدث مرة أخرى. أما مع معرّف لم يُرَ من قبل فيضيفه ثم يطبع type وcountry وfamily للحدث.

مثال تطبيقي: استقبال تسليم واحد

بالنسبة للحدث المسجَّل في 2026-09-25T15:44:56Z، تحدد الحمولة dataset.published والدولة MEX والعائلة labour. وتتضمن أيضًا إصدار المخطط ومعرّف الحدث والمؤشر ومعرّف المورد وإصداره.

حمولة حدث اقتصادي
{
  "schema_version": "1",
  "event_id": "...",
  "cursor": "1",
  "type": "dataset.published",
  "country": "MEX",
  "family": "labour",
  "resource_id": "...",
  "resource_version": "...",
  "recorded_at": "2026-09-25T15:44:56Z"
}

يصل التسليم مع ترويستي الطابع الزمني والتوقيع. يقرأ المستقبِل الطابع الزمني ويشترط أن يقل عمره عن 300 ثانية، ثم يعيد حساب HMAC للقيمة <timestamp>.<body>. وبعد تطابق التوقيع يحلّل الحمولة ويبحث عن event_id. وفي حالة حدث لم يُرَ من قبل يخزّن المعرّف ويطبع الحقول أدناه قبل أن يعيد 200.

ناتج المستقبِل
dataset.published MEX labour

نُشر في .

الخطوات التالية