تخطّي إلى المحتوى
المطوّرون · إشعارات

Webhooks — إشعارات لحظية بحالة كل توقيع.

استقبل الأحداث على خادمك لحظة وقوعها. فحين يتغيّر شيء، كأن يُفتح المستند أو تُؤكَّد الهوية أو يكتمل التوقيع، ترسل «وثائق» طلب POST موقَّعًا إلى عنوانك خلال ثوانٍ، فيستجيب نظامك فورًا دون الحاجة إلى الاستعلام المتكرّر عبر واجهة برمجة التطبيقات (API).

توقيع HMAC SHA-256 إعادة محاولة على مدى يومين تقريبًا 21 حدثًا
event delivery
خادم «وثائق»حدث في حسابك يُرسَل موقَّعًا
POST
signature_request.completed
خادمك · /hooks/wthaiqيتحقّق من التوقيع ثم يردّ 200 OK
Wthaiq-Signature t=1754500000,v1=6ff7d3f2b1a0c9e4d5f8a1b0c3a
كيف تعمل

أربع خطوات من الحدث إلى ردّ خادمك

نظام إشعارات الأحداث في «وثائق» بسيط ومتين: تُسجّل عنوانًا واحدًا يستقبل الأحداث التي تهمّك، ونتولّى نحن إرسالها إليه موقَّعةً كلّما تغيّر شيء. فلا حاجة إلى الاستطلاع الدوري (polling) ولا إلى الاستعلام المتكرّر، إذ يصلك الحدث فور وقوعه.

1

أنشئ نقطة استقبال (endpoint)

سجّل عنوان HTTPS عبر POST /v1/webhook_endpoints وحدّد الأحداث التي تشترك فيها ضمن enabled_events. يعيد الرد كائن webhook_endpoint يتضمّن السرّ whsec_ الذي ستستخدمه لاحقًا في التحقّق.

الحقلالنوعالوصف
urlstring مطلوبعنوان HTTPS يستقبل طلبات POST. يجب أن يكون متاحًا للعموم وأن يردّ بسرعة.
enabled_eventsstring[] مطلوبقائمة أنواع الأحداث التي تُرسَل إلى هذا العنوان. أدرِج ما تحتاجه فقط.
create_endpoint.sh
curl -X POST https://wthaiq.com/api/v1/webhook_endpoints \
  -H "Authorization: Bearer sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Wthaiq-Version: 2026-07-01" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://api.acme.com/hooks/wthaiq",
    "enabled_events": ["signature_request.completed", "signer.signed"]
  }'
webhook_endpoint · 201 Created
{
  "id": "we_1a",
  "object": "webhook_endpoint",
  "url": "https://api.acme.com/hooks/wthaiq",
  "enabled_events": ["signature_request.completed", "signer.signed"],
  "status": "enabled",
  "secret": "whsec_7bK2c8Vd1QpN9sR4tHmZ0xY",   // يظهر مرّة واحدة — احفظه بأمان
  "created_at": 1754000000
}
احفظ السرّ فورًا. لا تظهر قيمة whsec_ إلا مرّة واحدة عند الإنشاء. خزّنها في متغيّر بيئة آمن، فستحتاج إليها في كل عملية تحقّق. ولكل نقطة استقبال سرّها المستقل.
2

استقبل طلب POST

عند وقوع أي حدث اشتركت فيه، ترسل «وثائق» طلب POST إلى عنوانك، وجسمه بصيغة JSON كائن event يضمّ الكائن المتأثّر في data.object، مع ترويسة التوقيع Wthaiq-Signature.

incoming request
POST /hooks/wthaiq HTTP/1.1
Host: api.acme.com
Content-Type: application/json
Wthaiq-Version: 2026-07-01
Wthaiq-Signature: t=1754500000,v1=6ff7d3f2b1a0c9e4d5f8a1b0c3a...

{"id":"evt_2M8kQ1","object":"event","type":"signature_request.completed", ... }
3

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

قبل الوثوق بأي حمولة، أعِد حساب توقيع HMAC SHA-256 على الجسم الخام (raw body) وقارِنه بقيمة v1 في الترويسة، وارفض الطلب إن كان طابعه الزمني خارج نافذة السماح. وتجد التفاصيل والتنفيذ الكامل في قسم التحقّق من التوقيع أدناه.

4

ردّ بـ 2xx بسرعة

بعد التحقّق، أعِد رمز حالة في النطاق 2xx (مثل 200) خلال أقل من 5 ثوانٍ. وأي رمز خارج هذا النطاق يُعَدّ فشلًا في التسليم، فتُعاد المحاولة. أمّا العمل الثقيل، كتحديث قواعد البيانات وإرسال البريد وتوليد الملفات، فأجِّله إلى مهمّة غير متزامنة بعد إرسال الردّ.

القاعدة الذهبية: تحقّق من التوقيع، وخزّن معرّف الحدث، وردّ برمز 200 فورًا، ثم عالِج بقية العمل في الخلفية.
قائمة الأحداث

جميع أنواع الأحداث ومتى تُطلق

تنقسم الأحداث إلى مجموعات: أحداث على مستوى طلب التوقيع signature_request.*، وأحداث خاصة بكل موقّع signer.*، إلى جانب أحداث ختم المستند والتحقّق العام. اشترك في ما تحتاجه فقط عبر enabled_events.

نوع الحدثمتى يُطلق
signature_request.* — على مستوى طلب التوقيع
signature_request.createdأُنشئ طلب توقيع جديد في حسابك، وحُفظ مسودّةً قبل إرساله.
signature_request.sentأُرسل الطلب إلى الموقّعين، وأصبحت روابط التوقيع مفعّلة.
signature_request.viewedفتح أحد الموقّعين صفحة التوقيع الخاصّة بالطلب لأوّل مرّة، من رابط البريد أو واتساب أو جلسة التوقيع المضمّنة (signing_session).
signature_request.partially_signedوقّع أحد الموقّعين، وما يزال توقيع آخرين معلّقًا (في الطلبات متعدّدة الموقّعين).
signature_request.completedوقّع جميع الموقّعين واكتمل الطلب، فأصبح المستند المختوم متاحًا للتنزيل وأُسنِد إليه مرجع تحقّق عام.
signature_request.declinedرفض أحد الموقّعين التوقيع فتوقّف الطلب.
signature_request.expiredحلّ موعد انتهاء الصلاحية expires_at قبل اكتمال التوقيع.
signature_request.canceledألغيتَ الطلب عبر واجهة برمجة التطبيقات أو لوحة المطوّرين قبل اكتماله.
signer.* — على مستوى كل موقّع
signer.sentأُرسل رابط التوقيع إلى موقّع بعينه (ويُطلَق لكل موقّع عند حلول دوره إذا كان ترتيب التوقيع محدَّدًا).
signer.viewedفتح الموقّع صفحة التوقيع الخاصّة به لأوّل مرّة، من رابط البريد أو واتساب أو جلسة التوقيع المضمّنة (signing_session).
signer.otp_verifiedأدخل الموقّع بنجاح رمز التحقّق لمرّة واحدة (OTP) المُرسَل إلى بريده.
signer.identity_verifiedاجتاز الموقّع تأكيد الهوية في مستوى AES، بمستند رسمي ومطابقة حيّة للوجه عبر مزوّد متخصص في التحقّق من الهوية.
signer.identity_in_reviewأتمّ الموقّع خطوات تأكيد الهوية، وأُحيلت النتيجة إلى مراجعة بشرية، ولا يُتاح له التوقيع حتى يصدر القرار.
signer.identity_resubmission_requestedطلب المراجِع من الموقّع إعادة إحدى خطوات تأكيد الهوية، كتصوير المستند أو مطابقة الوجه، في الجلسة نفسها.
signer.identity_declinedرُفض تأكيد هوية الموقّع، آليًّا أو بعد المراجعة البشرية، ولا يُتاح له التوقيع ما لم يُعتمد تأكيد لاحق.
signer.identity_match_holdاجتاز الموقّع تأكيد الهوية، لكن الاسم في وثيقته يختلف عن الاسم المرسَل في الطلب، فأُوقف توقيعه حتى يقرّر مراجع الهوية في حسابك. يحمل الحدث الكائن identity_match بالاسمين ونوع الوثيقة وآخر أربعة أرقام منها.
signer.identity_match_decidedصدر القرار في اختلاف الاسم من لوحة المطوّرين أو عبر POST /signers/{id}/identity_match: approved فيُستكمل التوقيع، أو rejected فيُوقَف توقيع هذا الطرف.
signer.signedأتمّ الموقّع توقيعه على المستند.
signer.declinedرفض الموقّع التوقيع، مع إمكان ذكر السبب.
document.sealed و verification.created — الختم والتحقّق
document.sealedاكتملت جميع التواقيع، فخُتم للطلب محضر أدلّة موثَّق قابل للتحقّق المستقل، يحمل توقيع Ed25519 (يستطيع أي طرف التحقّق منه بالمفتاح العام في /trust) وختمًا زمنيًّا وفق RFC 3161. ولا يُضمَّن توقيع PAdES داخل ملف PDF في مسار واجهة برمجة التطبيقات. ولا يصدر لطلبات التجربة.
verification.createdأُنشئ سجلّ تحقّق عام برقم مرجعي (بصيغة WTQ-) يتيح التحقّق العلني من سلامة المستند. ولا يصدر لطلبات التجربة.
بنية الحمولة

غلاف الحدث والكائن المتأثّر

جسم كل إشعار يصلك هو كائن event موحَّد البنية يضمّ الكائن المتأثّر في data.object. ويدلّ الحقل type على نوع الحدث، و livemode يميّز الوضع الحيّ عن الاختبار، و created_at طابع زمني بصيغة Unix بالثواني.

event envelope
{
  "id": "evt_...",
  "object": "event",
  "type": "signature_request.completed",
  "created_at": 1754500000,
  "livemode": true,
  "data": {
    "object": { /* الكائن المتأثّر: signature_request أو signer أو … */ }
  }
}

مثال كامل للحدث signature_request.completed ومعه كائن signature_request بكامل حقوله في data.object:

signature_request.completed
{
  "id": "evt_2M8kQ1",
  "object": "event",
  "type": "signature_request.completed",
  "created_at": 1754500000,
  "livemode": true,
  "data": {
    "object": {
      "id": "sr_3n8Kd2Qa1V",
      "object": "signature_request",
      "livemode": true,
      "status": "completed",
      "title": "عقد عمل — أحمد م.",
      "legal_level": "aes",
      "format": "pades-lt",
      "source": { "type": "template", "template_id": "tpl_employment" },
      "signers": [
        {
          "id": "sgr_9fA2",
          "object": "signer",
          "name": "أحمد محمد",
          "email": "ahmed@example.com",
          "type": "individual",
          "method": "draw",
          "require_identity": true,
          "order": 1,
          "status": "signed",
          "signing_url": "https://wthaiq.com/sign.php?token=uZ8..",
          "viewed_at": 1754000100,
          "signed_at": 1754499900,
          "identity": { "status": "approved", "provider": "external", "level": "aes" },
          "fields": { "job_title": "مهندس برمجيات", "start_date": "2026-08-01" }
        }
      ],
      "ordered": true,
      "require_identity": true,
      "reference": "WTQ-1A2B3C4D5E6F",
      "reminders": { "enabled": true, "interval_hours": 48, "max": 3 },
      "expires_at": 1755000000,
      "completed_at": 1754500000,
      "download_url": "https://wthaiq.com/api/v1/signature_requests/sr_3n8Kd2Qa1V/download",
      "metadata": { "order_id": "A-1024" },
      "created_at": 1754000000
    }
  }
}
ملاحظة: تختلف بنية data.object بحسب type. فأحداث signer.* تحمل كائن signer، و document.sealed يحمل كائن signature_request، و verification.created يحمل كائن verification. اعتمد دائمًا على قيمة type لتحديد كيفية قراءة الحمولة.
التحقّق من التوقيع · حرِج

تحقّق من كل طلب قبل الوثوق به

ترسل «وثائق» مع كل طلب ترويسة Wthaiq-Signature تتيح لك التأكّد من أن الحمولة صادرة منّا وأنه لم يُعبَث بها. والتحقّق إلزامي: لا تعالِج أي حمولة قبل نجاحه.

مكوّنات الترويسة:
Wthaiq-Signature: t=1754500000,v1=<hmac_sha256 hex>
t = الطابع الزمني للإرسال بصيغة Unix (بالثواني). v1 = توقيع HMAC SHA-256 بالصيغة الست عشرية (hex).

خطوات التحقّق

الخطوةالتفصيل
1 · استخرجافصل t و v1 من قيمة الترويسة.
2 · ركّبالحمولة الموقّعة = "{t}.{raw_body}" — أي الطابع الزمني، ثم نقطة، ثم الجسم الخام حرفيًّا.
3 · احسباحسب HMAC-SHA256 للحمولة الموقّعة، على أن يكون المفتاح سرَّ نقطة الاستقبال whsec_، وأخرِج الناتج بالصيغة الست عشرية.
4 · قارِنقارِن الناتج بـv1 مقارنةً ثابتة الزمن (hash_equals / timingSafeEqual) لتفادي هجمات التوقيت.
5 · النافذةارفض الطلب إذا كان |now - t| > 300 ثانية (5 دقائق)، للحماية من هجمات إعادة الإرسال (replay).
استخدم الجسم الخام حرفيًّا. احسب HMAC على بايتات الجسم كما وصلت تمامًا، فأي إعادة ترميز، كتحليل JSON ثم إعادة تسلسله أو تغيير المسافات أو ترتيب المفاتيح، تُنتج بايتات مختلفة، فيفشل التحقّق ويُرفض طلب سليم. اقرأ الجسم الخام قبل أي طبقة وسيطة (middleware) تحلّل JSON تلقائيًّا.

تنفيذ التحقّق

Node.js Python PHP
webhook.js · Express
const express = require('express');
const crypto  = require('crypto');
const app = express();

const SECRET = process.env.WTHAIQ_WEBHOOK_SECRET; // whsec_...
const TOLERANCE = 300; // ثوانٍ

// مهم: استقبل الجسم الخام (Buffer)، لا JSON المُحلَّل
app.post('/hooks/wthaiq',
  express.raw({ type: 'application/json' }),
  (req, res) => {
    const raw    = req.body;                       // Buffer خام
    const header = req.get('Wthaiq-Signature') || '';

    // 1) استخرج t و v1
    const parts = Object.fromEntries(
      header.split(',').map(p => p.split('=')));
    const t = parts.t, v1 = parts.v1;

    // 2) نافذة الطابع الزمني (5 دقائق)
    const now = Math.floor(Date.now() / 1000);
    if (!t || Math.abs(now - Number(t)) > TOLERANCE)
      return res.status(400).send('timestamp out of tolerance');

    // 3) أعد حساب HMAC على "{t}.{raw_body}"
    const signedPayload = t + '.' + raw.toString('utf8');
    const expected = crypto
      .createHmac('sha256', SECRET)
      .update(signedPayload)
      .digest('hex');

    // 4) مقارنة ثابتة الزمن
    const ok = v1 && expected.length === v1.length &&
      crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(v1));
    if (!ok) return res.status(400).send('invalid signature');

    const event = JSON.parse(raw.toString('utf8'));
    // أزل التكرار على event.id، ثم ردّ فورًا وعالِج لاحقًا
    res.status(200).send('ok');
  });
webhook.py · Flask
import hmac, hashlib, time, os
from flask import Flask, request, abort

app = Flask(__name__)
SECRET = os.environ["WTHAIQ_WEBHOOK_SECRET"].encode()  # whsec_... كبايتات
TOLERANCE = 300  # ثوانٍ

@app.post("/hooks/wthaiq")
def handle():
    raw = request.get_data()               # بايتات خام — لا تستخدم request.json
    header = request.headers.get("Wthaiq-Signature", "")

    # 1) استخرج t و v1
    parts = dict(kv.split("=", 1) for kv in header.split(",") if "=" in kv)
    t, v1 = parts.get("t"), parts.get("v1")

    # 2) نافذة الطابع الزمني (5 دقائق)
    if not t or abs(time.time() - int(t)) > TOLERANCE:
        abort(400)

    # 3) أعد حساب HMAC على "{t}.{raw_body}"
    signed_payload = f"{t}.".encode() + raw
    expected = hmac.new(SECRET, signed_payload, hashlib.sha256).hexdigest()

    # 4) مقارنة ثابتة الزمن
    if not v1 or not hmac.compare_digest(expected, v1):
        abort(400)

    event = request.get_json()
    # أزل التكرار على event["id"]، ثم ردّ فورًا وعالِج لاحقًا
    return "", 200
webhook.php
<?php
$secret = getenv('WTHAIQ_WEBHOOK_SECRET');   // whsec_...
$tolerance = 300;                           // ثوانٍ

// اقرأ الجسم الخام — لا تستخدم $_POST مطلقًا
$raw    = file_get_contents('php://input');
$header = $_SERVER['HTTP_WTHAIQ_SIGNATURE'] ?? '';

// 1) استخرج t و v1
$parts = [];
foreach (explode(',', $header) as $kv) {
    [$k, $v] = array_pad(explode('=', $kv, 2), 2, '');
    $parts[$k] = $v;
}
$t  = $parts['t']  ?? '';
$v1 = $parts['v1'] ?? '';

// 2) نافذة الطابع الزمني (5 دقائق)
if ($t === '' || abs(time() - (int)$t) > $tolerance) {
    http_response_code(400);
    exit('timestamp out of tolerance');
}

// 3) أعد حساب HMAC على "{t}.{raw_body}"
$signedPayload = $t . '.' . $raw;
$expected = hash_hmac('sha256', $signedPayload, $secret);

// 4) مقارنة ثابتة الزمن
if ($v1 === '' || !hash_equals($expected, $v1)) {
    http_response_code(400);
    exit('invalid signature');
}

$event = json_decode($raw, true);
// أزل التكرار على $event['id']، ثم ردّ فورًا وعالِج لاحقًا
http_response_code(200);
echo 'ok';
لماذا المقارنة ثابتة الزمن؟ قد تنتهي مقارنة النصوص العادية مبكّرًا عند أوّل اختلاف، فتكشف فروقًا في التوقيت يمكن استغلالها لتخمين التوقيع. أمّا الدالّتان hash_equals و timingSafeEqual فتُجريان المقارنة في زمن ثابت أيًّا كان موضع الاختلاف.
إعادة المحاولة والموثوقية

ماذا يحدث عند الفشل، وكيف تبني معالجًا متينًا

إعادة المحاولة بتراجع أُسّي. إذا ردّ خادمك بأي رمز خارج نطاق 2xx (أو لم يردّ)، نعيد الإرسال تلقائيًّا وفق تراجع أُسّي (exponential backoff) على مدى يصل إلى نحو 46 ساعة. وبعد استنفاد المحاولات التسع يُصنَّف التسليم فاشلًا. ويمكنك إعادة إرسال أي إشعار بزرّ «إعادة إرسال الإشعار» في صفحة الإشعارات ببوابة المطوّرين أو عبر POST /v1/events/{id}/redeliver، واستدراك الأحداث الفائتة عبر GET /v1/events.
متى يخرج الإشعار؟ تخرج إشعارات العملية الواحدة فور وقوعها حتى عشرة إشعارات، وما زاد عنها يخرج خلال دقيقة تقريبًا.
اجعل المعالِج لا يتأثّر بالتكرار (idempotent). قد يصلك الحدث نفسه أكثر من مرّة (بسبب إعادة المحاولة أو تعذّر وصول ردّك). خزّن event.id (بصيغة evt_) وتجاهل أي معرّف سبق أن عالجته، لتبقى النتيجة واحدة مهما تكرّر التسليم.
ردّ خلال أقل من 5 ثوانٍ. ردّ برمز 2xx فور التحقّق من التوقيع وتخزين المعرّف، ثم انقل العمل الثقيل، كتحديث قواعد البيانات وإرسال البريد وتوليد الملفات، إلى طابور أو مهمّة غير متزامنة. فالمعالجة المتزامنة الطويلة تُبطئ الردّ، فتُحسَب فشلًا وتُعاد المحاولة.
الترتيب غير مضمون. قد تصل الأحداث بترتيب يخالف ترتيب وقوعها، فلا تفترض تسلسلًا معيّنًا، واستخدم created_at لترتيبها، وإذا احتجت إلى الحالة النهائية المؤكَّدة فاستعلِم عن أحدث نسخة عبر GET /v1/signature_requests/{id} بدل الاعتماد على حمولة الحدث وحدها.
الاختبار

اختبر تكاملك قبل الإطلاق

ابنِ تكاملك بمفتاح تجربة sk_test_؛ فأحداثه تحمل livemode:false ولا يُخصم به شيء ولا تُرسَل به رسالة إلى أحد. وسجّل نقطة الاستقبال بالمفتاح التجريبي نفسه عبر POST /v1/webhook_endpoints لتصلها أحداث التجربة، فالنقاط المضافة من لوحة المطوّرين حيّة. ثم أنشئ طلبًا تجريبيًّا ببريدك وافتح رابط التوقيع بنفسك، لتصلك أحداث signature_request.* و signer.* إلى نقطة استقبالك دون التأثير على عملاء حقيقيين.

أرسِل حدثًا حقيقيًّا لنفسك

أنشئ طلب توقيع بمفتاحك sk_ واجعل بريدك الخاص هو المستلم، فتصلك أحداث signature_request.* و signer.* إلى عنوانك لتتأكّد من وصولها ونجاح التحقّق منها، دون التأثير على عملاء حقيقيين، علمًا بأن تكلفة الطلب تُحتسَب فعليًّا.

أرسل حدثًا تجريبيًّا

أرسل من لوحة المطوّرين حدثًا تجريبيًّا إلى نقطة استقبالك لتتحقّق من التوقيع والمعالجة، واستدرك الأحداث السابقة بمعرّفاتها عبر GET /v1/events، وأعِد إرسال أي إشعار سابق بعينه من صفحة الإشعارات أو عبر POST /v1/events/{id}/redeliver.

افحص عمليات التسليم

تعرض لوحة المطوّرين آخر عمليات التسليم لكل نقطة: الحالة وعدد المحاولات ورمز الاستجابة ووقتها، لتشخّص أي فشل.

trigger_test_events.sh
# أنشئ طلبًا حقيقيًا صغيرًا (استخدم بريدك كمستلم) لتوليد أحداث حقيقية — يُحتسب إرسالًا من رصيد باقتك
curl -X POST https://wthaiq.com/api/v1/signature_requests \
  -H "Authorization: Bearer sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Wthaiq-Version: 2026-07-01" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "اختبار Webhook",
    "source": {"type":"template","template_id":"tpl_employment"},
    "legal_level": "ses",
    "signers": [{"name":"مطوّر","email":"you@example.com","method":"draw"}]
  }'
أفضل الممارسات

قائمة تحقّق الإنتاج

  • تحقّق من كل طلب قبل معالجته؛ لا تثق بأي حمولة غير موقّعة أو خارج نافذة الطابع الزمني.
  • استخدم الجسم الخام حرفيًّا في حساب HMAC، قبل أي تحليل JSON.
  • اجعل معالجتك لا تتأثّر بالتكرار بتخزين event.id وتجاهل المكرّر.
  • ردّ 2xx بسرعة ثم أنجز العمل الثقيل في مهمّة غير متزامنة.
  • لا تفترض ترتيبًا؛ واستعلِم عن أحدث حالة عبر واجهة برمجة التطبيقات إذا احتجت إلى التيقّن منها.
  • خصّص سرًّا لكل نقطة استقبال واحفظه في متغيّر بيئة، واستبدله دوريًّا.
  • افصل أحداث التجربة عن الحيّة. حقل livemode يساوي false في أحداث مفاتيح التجربة؛ فلا تُنفّذ عليها أثرًا حقيقيًّا في نظامك.
  • اشترك في ما تحتاجه فقط عبر enabled_events لتقليل الضجيج والحمل.
  • راقب عمليات التسليم من لوحة المطوّرين، وأعد الإرسال عند اللزوم.
  • استخدم HTTPS دائمًا ولا تكشف السرّ في السجلّات أو رسائل الخطأ.
أسئلة شائعة

أسئلة المطوّرين حول الـWebhooks

كيف أتحقّق من صحّة الطلب الوارد؟

أعِد حساب HMAC-SHA256 على الحمولة الموقّعة "{t}.{raw_body}" بمفتاح = سرّ نقطة الاستقبال whsec_، وقارِن الناتج hex بقيمة v1 في ترويسة Wthaiq-Signature بمقارنة ثابتة الزمن. ارفض الطلب إذا اختلف التوقيع أو كان الفارق بين الآن و t أكبر من 300 ثانية.

لماذا يجب استخدام الجسم الخام تحديدًا؟

لأن التوقيع محسوب على البايتات كما أُرسلت بالضبط. أي تحليل ثم إعادة تسلسل لـJSON قد يغيّر المسافات أو ترتيب المفاتيح أو ترميز المحارف، فتختلف البايتات ويفشل حساب HMAC ويُرفض طلب سليم. اقرأ الجسم الخام قبل أي middleware يحلّل JSON.

ماذا يحدث إذا فشل خادمي في الاستجابة؟

إذا ردّ خادمك بغير 2xx أو تجاوز المهلة، نعيد الإرسال وفق تراجع أُسّي على مدى يقارب 46 ساعة. بعد استنفاد المحاولات يُوسَم التسليم كفاشل، وتستطيع استدراك الأحداث الفائتة عبر GET /v1/events بعد إصلاح المشكلة.

هل ترتيب وصول الأحداث مضمون؟

لا. قد تصل الأحداث بترتيب مختلف عن ترتيب وقوعها، وقد يتكرّر الحدث نفسه. رتّب باستخدام created_at، وأزل التكرار على event.id، واستعلِم عن أحدث حالة عبر GET /v1/signature_requests/{id} عند الحاجة إلى اليقين.

كيف أختبر تكاملي قبل الإطلاق؟

استخدم مفتاح تجربة sk_test_ وسجّل به نقطة استقبالك عبر POST /v1/webhook_endpoints، ثم أنشئ طلبًا تجريبيًّا ببريدك وافتح رابط التوقيع بنفسك، فتصلك الأحداث بقيمة livemode: false دون أي خصم. وأرسل حدثًا تجريبيًّا من اللوحة، وافحص سجلّ عمليات التسليم لرؤية حالة كل محاولة ورمز الاستجابة.

جاهز لاستقبال أوّل حدث؟

أنشئ نقطة استقبال، وتحقّق من التوقيع، وابدأ التفاعل مع أحداث التوقيع لحظة وقوعها. ويقودك دليل البدء السريع من نقطة الصفر إلى تشغيل أوّل إشعار آلي خلال دقائق.