رفتن به محتوای اصلی
BirlesaBirlesa
بپرسید یا جست‌وجو کنیدورود
منو

وب‌هوک‌ها

وب‌هوک وقتی اتفاقی برای سازمان شما می‌افتد به سرورتان خبر می‌دهد: پیشنهاد قیمتی می‌رسد، مبلغ یک پرداخت امانی تأمین می‌شود، محموله‌ای به بندر می‌رسد. مالکان و مدیران وب‌هوک‌ها را در داشبورد یا با POST /v1/orgs/{org_id}/webhooks می‌سازند.

آنچه دریافت می‌کنید

هر ارسال یک POST با بدنهٔ کوچک JSON است:

{
  "id": "01a166eb-2f4e-74bd-a53e-6372d70ebead",
  "type": "quote.sent",
  "created_at": "2026-10-22T09:14:03.120000+00:00",
  "data": {"conversation_id": "01a166e9-…", "quote_id": "01a166eb-…"}
}
  • id شناسهٔ رویداد است. یک رویداد ممکن است بیش از یک بار برسد؛ آن را فقط یک بار پردازش کنید (شناسه‌هایی را که پردازش کرده‌اید نگه دارید).
  • data فقط شناسه‌ها را دارد؛ هرگز متن، نام، مبلغ یا اطلاعات افراد را. بقیه را با کلیدی که org:read دارد بخوانید، مثلاً GET /v1/org/quotes/{quote_id}. به این ترتیب همیشه بر پایهٔ وضعیت فعلی عمل می‌کنید، حتی وقتی رویدادها دیر یا نامرتب می‌رسند.
  • فهرست رویدادها همهٔ انواع رویداد و شناسه‌هایی را که هر کدام دارد نام می‌برد. هر وب‌هوک فقط انواعی را می‌گیرد که خواسته است.

سرآیندها:

سرآیندمقدار
Birlesa-Signaturet=<unix seconds>,v1=<hex signature> (در دورهٔ تعویض کلید محرمانه، دو مقدار v1)
Birlesa-Eventنوع رویداد
Birlesa-Deliveryشناسهٔ این ارسال؛ تلاش‌های دوبارهٔ یک ارسال همین شناسه را نگه می‌دارند
User-AgentBirlesa-Webhooks/1

هر ارسال را راستی‌آزمایی کنید

امضا HMAC-SHA256 روی <t>.<raw body> است. کلید آن کل کلید محرمانهٔ وب‌هوک است، همان‌طور که به شما نشان داده شد (brl_whsec_...). امضا را روی بایت‌های خام و پیش از تجزیهٔ JSON بررسی کنید. برچسب‌های زمانی قدیمی‌تر از پنج دقیقه را رد کنید تا ارسالی که رهگیری شده است نتواند بعداً دوباره پخش شود.

import hashlib
import hmac
import time

TOLERANCE_SECONDS = 300


def verify(secret: str, body: bytes, header: str) -> bool:
    fields = [part.split("=", 1) for part in header.split(",") if "=" in part]
    stamps = [value for name, value in fields if name == "t"]
    if len(stamps) != 1 or not stamps[0].isdigit():
        return False
    if abs(time.time() - int(stamps[0])) > TOLERANCE_SECONDS:
        return False
    expected = hmac.new(secret.encode(), f"{stamps[0]}.".encode() + body, hashlib.sha256)
    return any(
        hmac.compare_digest(expected.hexdigest(), value) for name, value in fields if name == "v1"
    )
import { createHmac, timingSafeEqual } from "node:crypto";

const TOLERANCE_SECONDS = 300;

export function verify(secret, rawBody, header) {
  const fields = header.split(",").map((part) => part.split("=", 2));
  const stamps = fields.filter(([name]) => name === "t").map(([, value]) => value);
  if (stamps.length !== 1 || !/^\d+$/.test(stamps[0])) return false;
  if (Math.abs(Date.now() / 1000 - Number(stamps[0])) > TOLERANCE_SECONDS) return false;
  const expected = createHmac("sha256", secret).update(`${stamps[0]}.`).update(rawBody).digest();
  return fields
    .filter(([name]) => name === "v1")
    .some(([, value]) => {
      const given = Buffer.from(value ?? "", "hex");
      return given.length === expected.length && timingSafeEqual(given, expected);
    });
}

سریع پاسخ دهید

ظرف ۱۰ ثانیه با هر 2xx پاسخ دهید و بعد کار را انجام دهید. ما بدنهٔ پاسخ شما را نمی‌خوانیم و تغییر مسیرها را دنبال نمی‌کنیم: 3xx شکست به حساب می‌آید.

تلاش دوباره و غیرفعال شدن

ارسالی که شکست بخورد (پاسخی نیاید، مهلت تمام شود، وضعیتی غیر از 2xx برگردد) پس از ۱، ۵ و ۱۵ دقیقه، ۱، ۳، ۶ و ۱۲ ساعت، و سپس دو بار دیگر با یک روز فاصله دوباره امتحان می‌شود: ده تلاش در حدود سه روز. پس از آن «ناموفق» علامت می‌خورد؛ می‌توانید آن را از تاریخچهٔ ارسال‌ها دوباره بفرستید (POST .../deliveries/{id}/retry).

وب‌هوکی که سه روز پیاپی و دست‌کم در ده تلاش شکست خورده باشد، غیرفعال می‌شود (status: disabled، disabled_reason: failing) و ارسال‌های در انتظار کنار گذاشته می‌شوند. اندپوینت را درست کنید، وب‌هوک را دوباره فعال کنید (PATCH با {"active": true}) و بعد ارسال‌هایی را که لازم دارید دوباره بفرستید.

نشانی‌هایی که به آن‌ها ارسال می‌کنیم

  • https:// روی پورت ۴۴۳، با یک نام میزبان عمومی. نشانی‌های IP، localhost، نام‌های داخلی (.local، .internal، .svc، ...) و اعتبارنامه در URL پذیرفته نمی‌شوند (webhook_url_refused؛ errors[0].code در سند problem دلیلش را می‌گوید: not_https، private_address، ...).
  • پیش از هر ارسال، نام میزبان دوباره به نشانی ترجمه (resolve) می‌شود. اگر هر یک از نشانی‌هایش خصوصی، loopback، link-local یا به هر شکل دیگری غیرعمومی باشد، چیزی فرستاده نمی‌شود (last_error: private_address).
  • گواهی امنیتی سرور باید برای همان نام میزبان معتبر باشد.

کلیدهای محرمانه

  • کلید محرمانه فقط یک بار، هنگام ساخت وب‌هوک، نمایش داده می‌شود. ما آن را مهروموم‌شده نگه می‌داریم و نمی‌توانیم دوباره نشانش دهیم.
  • POST .../rotate کلید محرمانهٔ تازه‌ای می‌دهد. تا ۲۴ ساعت، ارسال‌ها امضایی هم با کلید تازه و هم با کلید قدیمی دارند تا بتوانید کلید تازه را بدون از دست دادن هیچ ارسالی مستقر کنید. تعویض دوباره در همین ۲۴ ساعت پذیرفته نمی‌شود (rotation_pending).

آزمایش

POST .../test یک ارسال webhook.test (با data خالی) در صف می‌گذارد. این ارسال مثل هر ارسال دیگری فرستاده می‌شود، پس نتیجه‌اش همراه با وضعیتی که سرور شما برگرداند در تاریخچهٔ ارسال‌ها (GET .../deliveries) دیده می‌شود.

محدودیت‌ها

حداکثر ۱۰ وب‌هوک برای هر سازمان؛ حداکثر ۵ ارسال آزمایشی یا تلاش دوبارهٔ دستی در دقیقه. تاریخچهٔ ارسال‌ها ۳۰ روز نگه داشته می‌شود.

↵ انتخاب↑↓ حرکتesc بستن