الويب هوك
يُطلق في v1.2. العقد منشور حتى تبني عليه الآن؛ نقاط نهاية الويب هوك لم تُطلق بعد. تابع سجل التغييرات.
الويب هوك يخبر نظامك بأن شيئاً تغيّر — موظف أُضيف، أو مسيّر رواتب أُغلق — بدلاً من أن تسأل الـ API باستمرار. كل إرسال موقّع بسرّ نقطة النهاية.
سجّل نقطة نهاية
تحتاج صلاحية webhooks:manage. الرابط يجب أن يكون https:// على مضيف عام؛ العناوين الخاصة وعناوين الحلقة المحلية مرفوضة. الاستجابة تتضمن secret (يبدأ بـ whsec_) مرة واحدة فقط — احفظه. لتدوير السرّ احذف نقطة النهاية وأنشئها من جديد.
curl -X POST https://actions.ahlanhamad.com/v1/webhooks \
-H "Authorization: Bearer $AH_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://yourapp.example/webhooks/ahlan-hamad",
"events": ["employee.updated", "payroll_run.finalized"]
}'الأحداث
| الحدث | متى |
|---|---|
employee.created | أُضيف موظف |
employee.updated | تغيّر حقل يراه الشريك في بيانات موظف |
employee.left | غادر موظف |
payroll_run.finalized | أُغلق مسيّر رواتب ورُحّل قيده |
payroll_run.paid | عُلِّم مسيّر رواتب كمدفوع ورُحّل قيد الدفع |
payroll_run.corrected | تغيّرت مبالغ مسيّر مغلق — اقرأه من جديد |
payroll_run.deleted | حُذف مسيّر نهائي وعُكس قيده |
attendance.rejected | حركة حضور مخزّنة لم تتحول إلى حضور |
ping | حدث تجريبي |
شكل الإرسال
POST /webhooks/ahlan-hamad HTTP/1.1
Content-Type: application/json
X-AH-Signature: t=1759132931,v1=5f2b…
{
"id": "evt_…",
"type": "payroll_run.finalized",
"created_at": "2026-09-29T08:02:11Z",
"company_id": "k57f…",
"livemode": false,
"data": { … }
}id— معرّف فريد للحدث؛ استخدمه لإزالة التكرار.livemode—falseلشركات التجربة.data— الكائن المعني بشكله في الـ API. اقرأ الكائن المضمَّن ولا تعتمد على مقارنة الفروقات.
تحقق من التوقيع
كل إرسال يحمل الترويسة X-AH-Signature: t=<unix seconds>,v1=<hex>، حيث v1 = HMAC-SHA256(secret, "<t>.<raw body>"). تحقق على البايتات الخام للجسم قبل تحليل JSON، وارفض الإرسال إذا لم يطابق التوقيع أو إذا ابتعد t عن ساعتك أكثر من 5 دقائق. استخدم مقارنة ثابتة الزمن.
Node.js
import crypto from "node:crypto";
// rawBody: the exact bytes we sent, as a string — read it BEFORE JSON.parse
// (in Express: app.post(path, express.raw({ type: "application/json" }), …)).
export function verifyAhSignature(rawBody, header, secret, toleranceSec = 300) {
const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
const t = Number(parts.t);
if (!t || Math.abs(Date.now() / 1000 - t) > toleranceSec) return false;
const expected = crypto
.createHmac("sha256", secret)
.update(`${t}.${rawBody}`)
.digest("hex");
const given = Buffer.from(parts.v1 ?? "", "hex");
const want = Buffer.from(expected, "hex");
return given.length === want.length && crypto.timingSafeEqual(given, want);
}Python
import hashlib, hmac, time
def verify_ah_signature(raw_body: bytes, header: str, secret: str, tolerance: int = 300) -> bool:
parts = dict(p.split("=", 1) for p in header.split(","))
t = int(parts.get("t", "0"))
if abs(time.time() - t) > tolerance:
return False
expected = hmac.new(secret.encode(), f"{t}.".encode() + raw_body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, parts.get("v1", ""))PHP
function verify_ah_signature(string $rawBody, string $header, string $secret, int $tolerance = 300): bool {
parse_str(str_replace(',', '&', $header), $parts);
$t = (int)($parts['t'] ?? 0);
if (abs(time() - $t) > $tolerance) return false;
$expected = hash_hmac('sha256', $t . '.' . $rawBody, $secret);
return hash_equals($expected, $parts['v1'] ?? '');
}
// $rawBody = file_get_contents('php://input');
// $header = $_SERVER['HTTP_X_AH_SIGNATURE'] ?? '';التسليم وإعادة المحاولة
- مرة واحدة على الأقل، وليس بالضرورة بالترتيب. أزل التكرار بالاعتماد على
id. - أجب بـ
2xxخلال 10 ثوانٍ. أي حالة أخرى، أو عدم الرد، تُعاد محاولته. - نعيد المحاولة بعد: دقيقة، 5 دقائق، 30 دقيقة، ساعتان، 12 ساعة، 24 ساعة.
- بعد 7 أيام من الفشل تُعطَّل نقطة النهاية (
active: false،disabled_reason: "delivery_failures") ونرسل بريداً إلى مالك الشركة. بعد الإصلاح أعد تفعيلها بـPATCH. - تُعطَّل نقطة النهاية أيضاً إذا أُلغي مفتاح API الذي يملكها (
disabled_reason: "key_revoked").
| المحاولة | 2 | 3 | 4 | 5 | 6 | 7 |
|---|---|---|---|---|---|---|
| بعد الفشل السابق بـ | دقيقة | 5 دقائق | 30 دقيقة | ساعتان | 12 ساعة | 24 ساعة |
اختبر وصحّح بنفسك
POST /v1/webhooks/{id}/test— يرسل حدثpingعند الطلب.GET /v1/webhooks/{id}/deliveries— آخر 100 محاولة: ما أرسلناه، ورمز استجابتك، وأول 1 كيلوبايت من ردك، وموعد إعادة المحاولة. تُحذف المحاولات الأقدم من 30 يوماً.POST /v1/webhooks/{id}/deliveries/{delivery_id}/redeliver— يعيد إرسال الحدث نفسه (نفسidونفس المحتوى، بتوقيع ووقت جديدين).- وعند إطلاق الويب هوك يرى العميل السجل نفسه في أهلاً حمد تحت التكاملات ← مفاتيح API.