نمایک، نیاز اول کسب و کارها
پرش به محتوای اصلی

وب‌هوک‌ها

رویدادهای حساب و محصول‌ها را با درخواست امضاشده دریافت کنید؛ قالب دقیق، تایید امضا در چهار زبان و سیاست تلاش دوباره.

در این صفحه

کارکرد

وب‌هوک نشانی https سرور شماست که نمایک هنگام رخ دادن رویداد (کمبود موجودی، توقف سرویس، ابطال کلید و…) یک درخواست POST امضاشده به آن می‌فرستد. وب‌هوک را در پنل پروژه یا با API عمومی بسازید؛ راز امضا (whsec_…) فقط همان یک بار نمایش داده می‌شود.

curl -sS -X POST "https://api.namayek.com/v1/webhooks" \  -H "X-Namayek-Key: $NAMAYEK_SECRET_KEY" \  -H "Content-Type: application/json" \  -d '{"url":"https://example.com/hooks/namayek","events":["billing.low_balance","api_key.revoked"]}'

قالب درخواست

بدنه یک پاکت JSON با UTF-8 و حداکثر ۶۴ کیلوبایت است:

پاکت رویداد
{ "id": "evt_6sV0wQm2b3Xr8Lk1aZpQ9T", "type": "backup.succeeded", "created": "2026-10-23T10:00:00.000Z",  "apiVersion": "2026-10", "project": { "id": "<uuid>", "slug": "default" }, "data": { } }
فیلدهای پاکت رویداد
فیلدنوع / مقدارتوضیح
idstringشناسه رویداد (همان Namayek-Event-Id).
typestringنوع رویداد.
createdISO 8601زمان رخ دادن رویداد به UTC.
apiVersionstringنسخه قالب (اکنون 2026-10).
project{ id, slug }پروژه‌ای که رویداد مال آن است.
dataobjectداده ویژه هر رویداد؛ هرگز راز یا کلید در آن نیست.

سرآیندها

نمونه درخواست
POST /hooks/namayek HTTP/1.1Host: example.comContent-Type: application/json; charset=utf-8User-Agent: Namayek-Webhooks/1.0 (+https://namayek.com/docs/webhooks)Namayek-Event-Id: evt_6sV0wQm2b3Xr8Lk1aZpQ9TNamayek-Event-Type: billing.low_balanceNamayek-Delivery-Id: dlv_2N7xQ4pK9mR1Namayek-Attempt: 1Namayek-Signature: t=1792800000,v1=4d8512824d55c04b585dd61349811a29ec8305237211bcf43344d6c34850724d
سرآیندهای درخواست وب‌هوک
سرآیندنوع / مقدارتوضیح
Content-Typeapplication/json; charset=utf-8بدنه JSON با UTF-8، حداکثر ۶۴ کیلوبایت.
User-AgentNamayek-Webhooks/1.0 (+…/docs/webhooks)شناسه فرستنده.
Namayek-Event-Idevt_…شناسه یکتای رویداد؛ برای جلوگیری از پردازش تکراری.
Namayek-Event-Typestringنوع رویداد، مثلا billing.low_balance.
Namayek-Delivery-Idstringشناسه این تحویل (ارسال دوباره همان رویداد شناسه تازه دارد).
Namayek-Attempt1، 2، …شماره تلاش.
Namayek-Signaturet=…,v1=…[,v1=…]زمان یونیکس و امضای HMAC-SHA256؛ در دوره چرخش راز دو v1 می‌آید.

امضا

سرآیند Namayek-Signature زمان (t، ثانیه یونیکس) و یک یا چند امضای v1 دارد. امضا HMAC-SHA256 با کل راز (همراه پیشوند whsec_) روی t، یک نقطه و بدنه خام است:

فرمول امضا
v1 = hex( HMAC-SHA256( key = utf8(secret شامل "whsec_"), message = utf8(t + "." + rawBody) ) )
  • بدنه را خام (همان بایت‌ها، پیش از تجزیه JSON) امضا کنید.
  • با مقایسه زمان‌ثابت در برابر هر v1 تطبیق دهید؛ در دوره چرخش راز، امضای راز قبلی هم می‌آید.
  • اگر |now − t| > 300 ثانیه بود، درخواست را رد کنید (جلوگیری از تکرار).

این بردار آزمون را با پیاده‌سازی خود امتحان کنید:

بردار آزمون
secret = whsec_Tq7Rk2mWx9Lp4Vn8Bz3Hc6Jd1Fs5Gy0Et      = 1792800000body   = {"id":"evt_0000000000000000000001","type":"webhook.test"}v1     = 4d8512824d55c04b585dd61349811a29ec8305237211bcf43344d6c34850724d

نمونه کد تایید

import crypto from "node:crypto"; const TOLERANCE_SECONDS = 300; /** rawBody همان بایت‌های دریافتی است، پیش از JSON.parse */export function verifyNamayekSignature(rawBody, header, secret, now = Date.now()) {  const parts = String(header || "").split(",").map((p) => p.trim().split("="));  const t = Number(parts.find(([k]) => k === "t")?.[1]);  const signatures = parts.filter(([k, v]) => k === "v1" && /^[0-9a-f]{64}$/.test(v || "")).map(([, v]) => v);  if (!Number.isInteger(t) || signatures.length === 0) return false;  if (Math.abs(Math.floor(now / 1000) - t) > TOLERANCE_SECONDS) return false; // replay  const expected = crypto.createHmac("sha256", secret).update(`${t}.${rawBody}`, "utf8").digest();  return signatures.some((sig) => crypto.timingSafeEqual(expected, Buffer.from(sig, "hex")));} // Express: بدنه خام را نگه دارید// app.post("/hooks/namayek", express.raw({ type: "application/json" }), (req, res) => {//   if (!verifyNamayekSignature(req.body.toString("utf8"), req.get("Namayek-Signature"), process.env.NAMAYEK_WEBHOOK_SECRET)) {//     return res.sendStatus(400);//   }//   const event = JSON.parse(req.body.toString("utf8"));//   res.sendStatus(200); // سریع پاسخ دهید؛ پردازش در صف// });

تلاش دوباره

نتیجه هر پاسخ
پاسخ شمانتیجهتوضیح
2xx در مهلتموفقتحویل انجام شد؛ پاسخ را سریع بدهید و کار سنگین را در صف خودتان انجام دهید.
3xxناموفقتغییر مسیر هرگز دنبال نمی‌شود.
410 Goneپایانتحویل متوقف و وب‌هوک غیرفعال می‌شود.
دیگر پاسخ‌ها یا مهلتتلاش دوبارهطبق برنامه تلاش دوباره؛ پس از آخرین تلاش، تحویل «dead» می‌شود.

تلاش‌های بعدی با این فاصله از تلاش قبلی انجام می‌شوند (پیش‌فرض؛ تیم نمایک می‌تواند آن را تنظیم کند):

برنامه تلاش دوباره
تلاشفاصله از تلاش قبلی
21 دقیقه
35 دقیقه
430 دقیقه
52 ساعت
66 ساعت
712 ساعت
824 ساعت

پس از چند شکست پیاپی وب‌هوک خودکار غیرفعال و به شما اطلاع داده می‌شود. پس از رفع مشکل آن را فعال کنید و تحویل‌های ازدست‌رفته را از فهرست تحویل‌ها دوباره بفرستید.

رویدادها

رویدادهای وب‌هوک
رویدادمحصولتوضیح
webhook.testحسابرویداد آزمایشی از دکمه «آزمایش» پنل یا POST …/test.
wallet.chargedحسابشارژ آنلاین کیف پول نشست؛ مبلغ، شناسه تراکنش و سهمی که همان لحظه بدهی را تسویه کرد.
billing.low_balanceحسابموجودی کیف پول از آستانه هشدار کمتر شد.
billing.outstanding_createdحسابموجودی برای هزینه مصرف کافی نبود و بدهی ثبت شد؛ همراه با پایان مهلت پیش از توقف.
billing.suspendedحسابسرویس به دلیل بدهی متوقف شد.
billing.unsuspendedحسابسرویس دوباره فعال شد.
billing.statement_readyحسابصورت‌حساب ماه شمسی گذشته آماده شد؛ همراه با هزینه مصرف و بدهی پایان دوره.
project.spend_cap_reachedحسابسقف هزینه ماهانه پروژه پر شد.
api_key.expiringحسابکلیدی به‌زودی منقضی می‌شود.
api_key.revokedحسابکلیدی باطل شد.
backup.succeeded / failed / missedپشتیبان‌گیرینتیجه هر نوبت پشتیبان‌گیری.
restore.completed / failedپشتیبان‌گیرینتیجه بازگردانی.
agent.offline / onlineپشتیبان‌گیریقطع و وصل شدن عامل.

نکته‌های امنیتی

  • فقط نشانی https عمومی پذیرفته می‌شود؛ نشانی شبکه داخلی، localhost و IP خصوصی رد می‌شوند و تغییر مسیر دنبال نمی‌شود.
  • راز امضا را در متغیر محیطی نگه دارید و با چرخش (با دوره هم‌پوشانی تا ۷۲ ساعت) عوض کنید.
  • بدنه رویداد هرگز کلید، راز یا داده محرمانه ندارد؛ برای جزئیات بیشتر API را با کلید خودتان صدا بزنید.
  • سریع پاسخ ۲xx بدهید و پردازش سنگین را در صف خودتان انجام دهید تا مهلت تمام نشود.

آخرین به‌روزرسانی: