Watsy
الأساسياتكيف تعمل الـ Webhooks

الأساسيات

كيف تعمل الـ Webhooks

نرسل لخادمك طلباً موقّعاً عند كل تغيير مهم في عملية التحقق، فلا تحتاج للاستعلام المتكرر.

الإعداد

  1. من لوحة المشروع ← Webhooks أضف عنوان خادمك. يجب أن يكون https.
  2. اختر الأحداث التي تريدها، أو اتركها فارغة لتستقبل كل الأحداث.
  3. انسخ سر التوقيع (whsec_…). يظهر مرة واحدة عند الإنشاء.

يمكن إضافة حتى 10 عناوين لكل مشروع، وسجل الإرسال لكل عنوان ظاهر في نفس الصفحة.

الأحداث

الحدثمتى يُرسل
verification.sentوصل الرمز لقناة (أو في وضع الاختبار عند الإنشاء)
verification.verifiedفحص ناجح للرمز
verification.failedفشلت كل القنوات أو استُنفدت المحاولات. السبب في data.reason
verification.expiredانتهت الصلاحية دون تحقق

قيم reason في verification.failed: all_channels_failed، insufficient_balance، spend_cap_reached (توقف الإرسال في منتصف السلسلة)، وmax_attempts.

شكل الطلب

{
  "id": "evt_9f0c2d7a41b84e6f9c3a5b7d1e2f4a6c",
  "type": "verification.verified",
  "created_at": "2026-10-28T10:01:12.000Z",
  "data": {
    "id": "5d3c1f0e-7a1b-4c55-9a7e-2f1d6b0c9e11",
    "object": "verification",
    "mode": "live",
    "phone": "+9647701234567",
    "status": "verified",
    "channel": "telegram",
    "channels": [
      "whatsapp",
      "telegram",
      "sms"
    ],
    "attempts": 1,
    "max_attempts": 5,
    "attempts_remaining": 4,
    "locale": "ar",
    "metadata": {
      "user_id": 8841
    },
    "expires_at": "2026-10-28T10:05:00.000Z",
    "verified_at": "2026-10-28T10:01:12.000Z",
    "created_at": "2026-10-28T10:00:00.000Z"
  }
}

الحقول

  • idstringإلزامي

    معرّف الحدث (evt_…). استخدمه لتجاهل المكرر.

  • typestringإلزامي

    نوع الحدث.

  • created_atstringإلزامي

    وقت وقوع الحدث.

  • dataobjectإلزامي

    كائن verification كما في حالة عملية التحقق، بدون messages وprice_milli. يحمل metadata الخاصة بك، وreason في أحداث الفشل.

الترويسات

الترويسةالقيمة
Content-Typeapplication/json
User-AgentWatsy-Webhooks/1.0
Watsy-Eventنوع الحدث
Watsy-Deliveryمعرّف هذا الإرسال (يبقى نفسه عند إعادة المحاولة)
Watsy-Signaturet=<unix seconds>,v1=<hex>

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

v1 هو HMAC-SHA256 بالسر الخاص بك على النص "<t>.<الجسم الخام>" بصيغة hex. احسبه على البايتات الخام قبل تحليل JSON، وقارن بطريقة ثابتة الوقت، وارفض أي t أقدم أو أحدث من 5 دقائق.

import crypto from "node:crypto";
import express from "express";

const app = express();
const SECRET = process.env.WATSY_WEBHOOK_SECRET; // whsec_…

// Keep the raw body: the signature covers the exact bytes we sent.
app.post("/watsy/webhook", express.raw({ type: "application/json" }), (req, res) => {
  const raw = req.body.toString("utf8");
  const header = req.get("Watsy-Signature") ?? "";
  const parts = Object.fromEntries(
    header.split(",").map((kv) => {
      const i = kv.indexOf("=");
      return [kv.slice(0, i).trim(), kv.slice(i + 1).trim()];
    }),
  );
  const t = Number(parts.t);
  if (!Number.isInteger(t) || Math.abs(Date.now() / 1000 - t) > 300) return res.sendStatus(400);

  const expected = crypto.createHmac("sha256", SECRET).update(`${t}.${raw}`).digest();
  const given = Buffer.from(parts.v1 ?? "", "hex");
  if (given.length !== expected.length || !crypto.timingSafeEqual(given, expected)) {
    return res.sendStatus(400);
  }

  const event = JSON.parse(raw);
  // Deduplicate by event.id, then handle event.type / event.data.
  res.sendStatus(200);
});

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

  • أرجع أي رد 2xx خلال 5 ثوانٍ. عالج الحدث بعد الرد إذا كان يحتاج وقتاً.
  • عند الفشل نعيد بعد 1 دقيقة، ثم 5 دقائق، ثم 30 دقيقة، ثم ساعتين، ثم 6 ساعات. بعد 6 محاولات يُعلَّم الإرسال failed.
  • لا نتبع التحويلات (redirects): الرد 3xx يُعتبر فشلاً.
  • قد يصل الحدث نفسه أكثر من مرة، وقد تصل الأحداث بغير ترتيبها. اعتمد على id وdata.status.

العنوان يجب أن يكون عاماً: لا نرسل إلى localhost أو عناوين الشبكات الخاصة في الإنتاج.